mirror of
https://github.com/razzant/ouroboros.git
synced 2026-10-03 20:27:56 +00:00
executor: a hash-less foreground record falls back to liveness on Windows only (C6 R7 consequence)
_host_pid_matches_record fell back to pid_is_alive whenever a record carried no
host_command_sha256. The fallback exists for Windows, where no command line can be
captured, but it was reachable on POSIX by an owner-shaped forged record -- and
since round 5.4 EPERM reads alive (the process exists), so kill_all_foreground
signalled pid 1 for such a record. The serial lane caught it
(tests/test_workspace_executor_cleanup.py::test_executor_cleanup_ignores_owner_shaped_forged_host_pid_records,
red at 9faccf31). On POSIX a registered foreground process always carries its
command hash: an empty one is not ours to kill -- liveness is not ownership.
Every other pid_is_alive consumer defers on an alive pid; none signals on
liveness alone (ledger section «From the C6 integration battery»).
This commit is contained in:
parent
9faccf31fd
commit
504bb20c6d
2 changed files with 15 additions and 9 deletions
|
|
@ -7667,3 +7667,7 @@ Three read-only lenses on the 5.4 delta: NEEDS_FIXES × 3, no HIGH (3 MEDIUM, 7
|
|||
3. **R3 stamp-less symlink levels (LOW) — fixed**, pin deferred (suite at its 1600-line cap; mutation-verified by hand). **R5 witness inode tie + `_Abort` on a vanished ledger (LOW) — fixed**, no pin (disclosed). **R6 heartbeat-ownership pin (LOW) — disclosed, not fixed.** **R1 uncached unprobeable directory, R7 shared liveness primitive, R8d epoch-floor duration, R8e socket shape (LOW) — docs corrected** in DESIGN §8/§10/§12.5, packet §5.9/§5.10/§9/§10, ARCHITECTURE row.
|
||||
|
||||
Gates at the close-out tip: `tests/test_lockfile_helpers.py` + `tests/test_usage_compaction.py` + `tests/test_usage_*` + `tests/test_persistence_inventory.py` rc 0 (107 passed, 1 skipped Windows-only); `ruff --select F` rc 0; `scripts/check_domains.py` rc 0; `scripts/regenerate_size_ratchet.py --check` rc 0 (`platform_layer.py` 1500, `tests/test_usage_compaction.py` 1600 — both AT their ceilings); `git diff --check` rc 0. The CI-shape battery and `-m serial` run on the integration tree after the lane merges.
|
||||
|
||||
## From the C6 integration battery (serial lane on 9faccf31)
|
||||
|
||||
The serial lane found the one consumer of `pid_is_alive` the round-5.4 R7 flip (EPERM → alive) turned from "defer" into "signal": `ouroboros/workspace_executor._host_pid_matches_record` fell back to liveness whenever a foreground record had no `host_command_sha256` — a fallback written for Windows (no command line there) but reachable on POSIX by an owner-shaped FORGED record — so `kill_all_foreground` signalled pid 1 (`tests/test_workspace_executor_cleanup.py::test_executor_cleanup_ignores_owner_shaped_forged_host_pid_records`, red at 9faccf31: «owner-shaped forged record should be ignored»). The fallback is now Windows-only: on POSIX a registered process always carries its command hash, so an empty hash is not ours to kill — liveness is not ownership. Other consumers re-checked: process_custody, launcher_server_reaper, delegate_recovery, cancel_intents, extension_import_staging, review_owner_custody, skill_review_runner, agent_startup_checks all DEFER (keep/skip) on an alive pid; none signals on liveness alone. Executor suites (serial + non-serial), process custody and zombie prevention green after the fix; the packet §9 R7 disclosure stands with this consumer named.
|
||||
|
|
|
|||
|
|
@ -603,15 +603,17 @@ def _host_pid_matches_record(record: dict[str, Any]) -> bool:
|
|||
return False
|
||||
expected = str(record.get("host_command_sha256") or "").strip()
|
||||
if not expected:
|
||||
# No command line could be captured at register time. This is always the
|
||||
# case on Windows, where platform_layer.process_command() is POSIX-only
|
||||
# and returns "". Without this fallback the record would be permanently
|
||||
# unvalidatable, so kill_all_foreground/_services would never dispatch
|
||||
# taskkill for it (the worktree/service cleanup leak). Fall back to a
|
||||
# liveness check; owner/schema/id are already verified by the caller
|
||||
# (_valid_process_record). The PID-reuse hardening via command-hash
|
||||
# comparison still applies on POSIX, where a command line is available.
|
||||
return pid_is_alive(host_pid)
|
||||
# No command line could be captured at register time. On Windows that is
|
||||
# ALWAYS the case (platform_layer.process_command() is POSIX-only), and
|
||||
# without a fallback the record would be permanently unvalidatable, so
|
||||
# kill_all_foreground/_services would never dispatch taskkill for it (the
|
||||
# worktree/service cleanup leak): there, fall back to liveness — owner/
|
||||
# schema/id are already verified by the caller (_valid_process_record).
|
||||
# On POSIX a registered process always has a command line, so an empty
|
||||
# hash is not ours to kill: liveness is NOT proof of ownership — EPERM
|
||||
# reads alive (C6 round 5.4), so an owner-shaped forged record naming a
|
||||
# foreign pid would otherwise be signalled. Fail safe: no hash, no kill.
|
||||
return IS_WINDOWS and pid_is_alive(host_pid)
|
||||
return _process_command_sha256(host_pid) == expected
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue