Close Panama recovery race windows

This commit is contained in:
Gabriel Brown
2026-08-18 11:11:53 -04:00
parent 8bfae70284
commit faa9a00716
2 changed files with 315 additions and 55 deletions
+156 -23
View File
@@ -259,12 +259,14 @@ summary="$(run_doctor --summary)"
# Repairs run against a second, disposable Panama root. Every process boundary
# records its argv, and every filesystem assertion is confined to this fixture.
repair_root="$fixture/repair-root"
repair_root="$home/.local/share/Panama"
repair_log="$runtime_dir/repair.log"
mkdir -p "$repair_root/config/dot" "$repair_root/setup/scripts"
mkdir -p "$repair_root/config/dot" "$repair_root/config/local/share/vicinae/scripts" \
"$repair_root/setup/scripts"
for name in hypr quickshell uwsm vicinae; do
mkdir -p "$repair_root/config/dot/$name"
done
cp "$repo_dir/setup/scripts/link-vicinae-scripts" "$repair_root/setup/scripts/link-vicinae-scripts"
mv "$bin_dir/systemctl" "$bin_dir/systemctl-probe"
cat >"$bin_dir/systemctl" <<'EOF'
@@ -309,8 +311,14 @@ if [[ "$mode" == disappear && "$count" -ge 2 ]]; then
/usr/bin/sleep 0.01
done
fi
printf 'Panama %s fixture-user %s systemd-inhibit sleep:idle Caffeine block\n' "$uid" "$preserved"
printf 'Panama %s fixture-user %s systemd-inhibit sleep:idle Caffeine block\n' "$uid" "$preserved"
preserved_comm=systemd-inhibit
if [[ "$mode" == preserve-altered && "$count" -ge 2 ]]; then
preserved_comm=changed-command
fi
printf 'Panama %s fixture-user %s %s sleep:idle Caffeine block\n' "$uid" "$preserved" "$preserved_comm"
if [[ "$mode" != multiplicity || "$count" -lt 2 ]]; then
printf 'Panama %s fixture-user %s systemd-inhibit sleep:idle Caffeine block\n' "$uid" "$preserved"
fi
if [[ "$mode" == altered && "$count" -ge 2 ]]; then
printf 'Panama %s fixture-user %s systemd-inhibit sleep:idle Other block\n' "$uid" "$duplicate"
else
@@ -322,28 +330,19 @@ printf 'Panama %s fixture-user 4997 systemd-inhibit sleep:idle Other block\n' "$
printf 'Panama %s fixture-user 4996 systemd-inhibit sleep:idle Caffeine delay\n' "$uid"
EOF
cat >"$repair_root/setup/scripts/link-vicinae-scripts" <<'EOF'
#!/usr/bin/bash
set -euo pipefail
printf 'link-vicinae-scripts|%s' "$0" >>"$XDG_RUNTIME_DIR/repair.log"
if (( $# > 0 )); then
printf '|%s' "$@" >>"$XDG_RUNTIME_DIR/repair.log"
fi
printf '\n' >>"$XDG_RUNTIME_DIR/repair.log"
EOF
chmod +x "$bin_dir/systemctl" "$bin_dir/panama-action" \
"$bin_dir/systemd-inhibit" "$repair_root/setup/scripts/link-vicinae-scripts"
run_repair() {
HOME="$home" \
PATH="$bin_dir" \
PATH="$bin_dir:/usr/bin" \
XDG_CURRENT_DESKTOP=Hyprland \
PANAMA_DOCTOR_ROOT="$repair_root" \
PANAMA_DOCTOR_HOME="$home" \
PANAMA_DOCTOR_CONFIG_HOME="$config_home" \
PANAMA_DOCTOR_STATE_HOME="$state_home" \
PANAMA_DOCTOR_RUNTIME_DIR="$runtime_dir" \
PANAMA_DOCTOR_PATH="$bin_dir" \
PANAMA_DOCTOR_PATH="$bin_dir:/usr/bin" \
PANAMA_DOCTOR_TIMEOUT=0.2 \
/usr/bin/python3 "$doctor" "$@"
}
@@ -397,13 +396,19 @@ assert_repair_result desktop.vicinae true 5
[[ "$(<"$repair_log")" == 'systemctl|--user|restart|vicinae.service' ]] \
|| fail 'failed repair changed the authored argv'
# The Vicinae repair executes only the authored setup helper with no arguments.
: >"$repair_log"
# The real authored Vicinae helper converges the exact child link diagnosed by
# panama-doctor under the isolated HOME.
rm -f "$data_home/vicinae/scripts/panama"
before_vicinae_repair="$(run_repair --json)"
check_status "$before_vicinae_repair" panama.vicinae-commands warning
invoke_repair panama.vicinae-commands
[[ "$repair_status" == 0 ]] || fail "Vicinae command repair returned $repair_status"
assert_repair_result panama.vicinae-commands true 0
[[ "$(<"$repair_log")" == "link-vicinae-scripts|$repair_root/setup/scripts/link-vicinae-scripts" ]] \
|| fail "Vicinae command repair argv was not exact: $(<"$repair_log")"
after_vicinae_repair="$(run_repair --json)"
check_status "$after_vicinae_repair" panama.vicinae-commands ok
[[ -L "$data_home/vicinae/scripts/panama" \
&& "$(readlink "$data_home/vicinae/scripts/panama")" == "$repair_root/config/local/share/vicinae/scripts" ]] \
|| fail 'Vicinae repair did not install the diagnosed child link'
# Runtime-link repair may replace only absent links or symlinks whose lexical
# target proves Panama ownership. Every other object remains untouched.
@@ -448,7 +453,7 @@ invoke_repair panama.runtime-links
[[ -d "$config_home/vicinae" && ! -L "$config_home/vicinae" ]] \
|| fail 'runtime-link repair replaced a user-owned directory'
# An injected os.replace failure occurs after the authored temporary symlink is
# An injected exchange failure occurs after the authored candidate symlink is
# made; the original link must still be intact.
/usr/bin/python3 - "$doctor" "$repair_root" "$fixture/atomic-config" <<'PY' \
|| fail 'atomic replacement failure did not preserve the original link'
@@ -471,18 +476,66 @@ destination = config_home / "hypr"
original = root / "config/dot/quickshell"
destination.symlink_to(original, target_is_directory=True)
config = module.DoctorConfig(root, config_home.parent, config_home, config_home.parent / "state", config_home.parent / "runtime", "", 0.2)
real_replace = module.os.replace
module.os.replace = lambda source, target: (_ for _ in ()).throw(OSError("fixture replacement failure"))
real_exchange = module.rename_exchange
module.rename_exchange = lambda source, target: (_ for _ in ()).throw(OSError("fixture exchange failure"))
try:
result = module.repair_runtime_links(config)
finally:
module.os.replace = real_replace
module.rename_exchange = real_exchange
assert result.exit_code == 1
assert destination.is_symlink()
assert os.readlink(destination) == str(original)
assert not list(config_home.glob(".panama-link-*"))
PY
# A deterministic swap at the ownership/replacement boundary must be detected
# from the exchanged-out object and rolled back, preserving the external link.
/usr/bin/python3 - "$doctor" "$repair_root" "$fixture/toctou-config" "$fixture/external-race-target" <<'PY' \
|| fail 'runtime-link exchange did not restore a boundary-swapped external link'
import importlib.machinery
import importlib.util
import os
import sys
from pathlib import Path
doctor_path, root_text, config_text, external_text = sys.argv[1:]
loader = importlib.machinery.SourceFileLoader("panama_doctor_toctou", doctor_path)
spec = importlib.util.spec_from_loader(loader.name, loader)
module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module
loader.exec_module(module)
root = Path(root_text)
config_home = Path(config_text)
config_home.mkdir(parents=True)
for name, relative in module.RUNTIME_LINK_TARGETS:
(config_home / name).symlink_to(root / relative, target_is_directory=True)
destination = config_home / "uwsm"
destination.unlink()
destination.symlink_to(root / "config/dot/quickshell", target_is_directory=True)
external = Path(external_text)
real_exchange = module.rename_exchange
first = True
def race_exchange(candidate, target):
global first
if first:
first = False
target.unlink()
target.symlink_to(external, target_is_directory=True)
real_exchange(candidate, target)
module.rename_exchange = race_exchange
config = module.DoctorConfig(root, config_home.parent, config_home, config_home.parent / "state", config_home.parent / "runtime", "", 0.2)
try:
result = module.repair_runtime_links(config)
finally:
module.rename_exchange = real_exchange
assert result.exit_code == 1
assert destination.is_symlink()
assert os.readlink(destination) == str(external)
assert not list(config_home.glob(".panama-link-*"))
PY
# Caffeine repair deduplicates rows, pins each distinct duplicate with a
# pidfd, revalidates authored metadata, and signals only the duplicate.
/usr/bin/sleep 30 &
@@ -524,6 +577,86 @@ assert_repair_result panama.caffeine true 1
kill -0 "$altered_preserved" >/dev/null 2>&1 || fail 'metadata refusal signaled the preserved process'
kill -0 "$altered_duplicate" >/dev/null 2>&1 || fail 'metadata refusal signaled the candidate process'
# Changing metadata on the preserved row is also a full-identity mismatch,
# even though every duplicate PID remains present.
/usr/bin/sleep 30 &
preserve_changed_keep=$!
child_pids+=("$preserve_changed_keep")
/usr/bin/sleep 30 &
preserve_changed_duplicate=$!
child_pids+=("$preserve_changed_duplicate")
printf '%s %s\n' "$preserve_changed_keep" "$preserve_changed_duplicate" >"$runtime_dir/caffeine-pids"
printf 'preserve-altered\n' >"$runtime_dir/caffeine-mode"
rm -f "$runtime_dir/caffeine-list-count"
invoke_repair panama.caffeine
[[ "$repair_status" == 1 ]] || fail 'preserved-row metadata change was not safely refused'
assert_repair_result panama.caffeine true 1
kill -0 "$preserve_changed_keep" >/dev/null 2>&1 || fail 'preserved-row mismatch signaled the preserved process'
kill -0 "$preserve_changed_duplicate" >/dev/null 2>&1 || fail 'preserved-row mismatch signaled the duplicate process'
# A repeated exact row disappearing between lists changes multiplicity and is
# refused before signaling any pinned duplicate.
/usr/bin/sleep 30 &
multiplicity_keep=$!
child_pids+=("$multiplicity_keep")
/usr/bin/sleep 30 &
multiplicity_duplicate=$!
child_pids+=("$multiplicity_duplicate")
printf '%s %s\n' "$multiplicity_keep" "$multiplicity_duplicate" >"$runtime_dir/caffeine-pids"
printf 'multiplicity\n' >"$runtime_dir/caffeine-mode"
rm -f "$runtime_dir/caffeine-list-count"
invoke_repair panama.caffeine
[[ "$repair_status" == 1 ]] || fail 'inhibitor row multiplicity change was not safely refused'
assert_repair_result panama.caffeine true 1
kill -0 "$multiplicity_keep" >/dev/null 2>&1 || fail 'multiplicity mismatch signaled the preserved process'
kill -0 "$multiplicity_duplicate" >/dev/null 2>&1 || fail 'multiplicity mismatch signaled the duplicate process'
# The production pidfd release function preflights every candidate before any
# SIGTERM. A refused second preflight leaves both disposable children alive.
/usr/bin/sleep 30 &
preflight_first=$!
child_pids+=("$preflight_first")
/usr/bin/sleep 30 &
preflight_second=$!
child_pids+=("$preflight_second")
/usr/bin/python3 - "$doctor" "$preflight_first" "$preflight_second" <<'PY' \
|| fail 'pidfd preflight failure signaled a disposable duplicate'
import errno
import importlib.machinery
import importlib.util
import os
import signal
import sys
doctor_path = sys.argv[1]
pids = [int(value) for value in sys.argv[2:]]
loader = importlib.machinery.SourceFileLoader("panama_doctor_preflight", doctor_path)
spec = importlib.util.spec_from_loader(loader.name, loader)
module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module
loader.exec_module(module)
pidfds = [os.pidfd_open(pid, 0) for pid in pids]
calls = []
def sender(pidfd, sig, siginfo, flags):
calls.append(sig)
if sig == 0 and pidfd == pidfds[1]:
raise PermissionError(errno.EPERM, "fixture preflight refusal")
signal.pidfd_send_signal(pidfd, sig, siginfo, flags)
try:
outcome = module.signal_caffeine_pidfds(pidfds, sender)
finally:
for pidfd in pidfds:
os.close(pidfd)
assert outcome == "preflight-failed"
assert calls == [0, 0]
for pid in pids:
os.kill(pid, 0)
PY
kill -0 "$preflight_first" >/dev/null 2>&1 || fail 'preflight refusal killed the first duplicate'
kill -0 "$preflight_second" >/dev/null 2>&1 || fail 'preflight refusal killed the second duplicate'
# A candidate that disappears after pidfd acquisition and second-list request
# is a safe failure; an unrelated disposable process must remain untouched.
/usr/bin/sleep 30 &