Repository navigation
test(harness): parent cleanup, join gate, fault seams - #2685
Conversation
macOS bash 3.2 reports -c input as line 0, modern GNU bash as line 1. Compare diagnostic shape with line number normalized.
…gression A long -c ARG0 stamped on every report line could evict the structural payload out of the 1 KiB diagnostic budget. Truncate the shell name to 64 chars first so 'syntax error ...' always survives; renumber the threat entry to TM-DOS-136 (main already owns 135 via #2674); add a bounded-child regression exercising the exact finding shape (multiline token x long ARG0) at two scales.
The 64-char cut mangled long script paths (breaking the oracle's path
replacement and hiding which script failed). Widen to 256 chars with a
16-char tail: realistic paths print verbatim with attribution suffixes
(': eval') intact, while a kilobyte-scale ARG0 still cannot evict the
structural payload out of the 1 KiB total. The total stays the enforced
bound.
The scaled multiline-token x long-ARG0 regression now asserts the whole harness contract at both scales (200/2000 lines x 8 KiB ARG0): no watchdog timeout, raw child exit 2, no output-cap overshoot, finished reader threads, and effective stack=4096/cpu=10 caps read back from the launcher. Shape and 1 KiB assertions unchanged.
run_command_bounded cleanup per lifecycle review: truncate retention before extending (retention never passes the 8192 cap); retry Interrupted, fail closed with partial bytes on real reader errors (new read_error field, never relabeled as EOF); exact-cap EOF is not excess; reap inside a finite 5s grace, never an unbounded wait; timed_out retained as recorded; drain 2s + reap 5s graces documented against the execution deadline. Consumer regression gains !read_error and the corrected 8192-byte cap text. Bounded selftests: exact-cap, cap+1, Interrupted/error scripted pipes, timeout kill+reap, overflow flag.
Per lifecycle review on #2682: join readers only after all channel receipts (receipt proves all blocking work done; missing receipts skip the join and fail incomplete), with panicking readers failing visibly; reap extracted into a poll seam with deterministic verdict unit tests; timeout/overflow selftests run under the fail-closed launcher with stack/cpu readback and elapsed-total bounds; retained-pipe case gains elapsed proof and read_error distinction. Reuses existing bounded_helper_flags_retained_pipes; does not touch #2681's distinct probe helper.
…tests Per completion review on #2683: join readers only while is_finished inside the shared completion grace (a receipt is not the termination contract); deterministic paused-after-send proof with release/cleanup watchdog; poll/read/reap faults injected through the real parent lifecycle with distinct fail-closed verdicts (no panicking expect on child poll); timeout/overflow selftests under the fail-closed launcher with cap readback and elapsed bounds; existing retained-pipe fixture reused and strengthened. Untouched: #2681 distinct probe helper.
…tests Per completion review on #2683: join readers only while is_finished inside the shared completion grace (a receipt is not the termination contract); deterministic paused-after-send proof with release/cleanup watchdog; poll/read/reap faults injected through the real parent lifecycle with distinct fail-closed verdicts (no panicking expect on child poll); timeout/overflow selftests under the fail-closed launcher with cap readback and elapsed bounds; existing retained-pipe fixture reused and strengthened. Untouched: #2681 distinct probe helper.
… selftests Per completion review on #2684: common cleanup termination funnels every loop exit through one reachable kill guard; bounded is_finished join gate with shared completion grace (receipt is not the termination contract); deterministic paused-after-send proof drives the actual gate with bounded readiness and release/cleanup watchdog; poll/read/reap faults injected through the real parent lifecycle with distinct fail-closed verdicts (no panicking child poll); selftests and retained-pipe fixture under the fail-closed launcher with cap readback and elapsed bounds. Reuses existing retained-pipe fixture; untouched: #2681 distinct probe helper.
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
bashkit | 2511659 | Commit Preview URL Branch Preview URL |
Oct 11 2026, 12:00 PM |
|
Review of PR2685 head25116591d891cc81185079acd1199848e75a2846. Common reachable cleanup kill and unconditional real OS reap before injected observation suppression are useful fixes; preserve them. This is not a claim of a reproduced old hang.
Read complete issue comments and formal/inline review threads before merge, not formalreviews=[] alone. Keep both scaled ARGV assertions/cap-before-copy/readerror/exactcap/finite is_finished gate. Obtain actual producer exits rather than cargo|tail pipeline0, unchanged full Linux CI and fresh exactmerged proofs/root report before finding closure. The watcher currently exits0 on zero pending even with failed checks, so independently inspect required conclusions; pending CI is not a stall. Preserve all branch/bench/stash/history and use additive normal commits only. |
Per completion review on #2685: fail_poll fires only after a readiness file proves the victim truly live (direct-exec sleeper, no orphan); BoundedRun gains a reaped receipt separating actual OS cleanup from exit/observation verdicts; retained-pipe fixture runs under the fail-closed launcher with a file side-channel receipt proving the effective bounds despite incomplete pipe capture. Reuses the existing retained-pipe fixture; untouched: #2681 distinct probe helper.
…receipts (#2686) ## What changed Test-only, one file: threat_model_tests.rs. Closes the completion review on #2685 without touching #2681 distinct run_bounded_probe helper: - join_finished returns the unfinished handle, so the paused-after-send proof releases and joins THAT SAME thread through the actual gate primitive (bounded readiness, release, finite join cleanup, elapsed watchdog). No twin thread, no detached-while-asserting-cleanup. - Poll-fault fixture uses a direct-exec sleeper: the shell replaces itself, so killing closes the pipes at once with no orphan and no 30s descendant. - reap_never keeps the real bounded OS cleanup while only the observation is discarded for the verdict: no zombies by construction, no invented exit/reap success. - Retained-pipe fixture runs under the fail-closed launcher; because incomplete pipe capture precludes the stderr readback by design, the effective bounds are proven through a per-process receipt file the fixture shell writes before parking the sleeper (removed afterwards). Existing fixture reused and strengthened, never duplicated. - Prior review debt acknowledged: #2684 merged on formal reviews alone while the completion comment was unresolved; this followup resolves it before any merge. ## Before / After Before: twin-thread gate proof; forked sleeper orphan in the poll fixture; reap skipped under fault; no launcher proof on the retained fixture. After: threat_model 222 passed (only pre-existing macOS nesting abort skipped, identical on unmodified code); error 14, partial_parse 11 green; fmt plus clippy -D warnings clean. ## Risk - Test-only change. No production code touched. ## Checklist - [x] Tests added or updated - [x] Backward compatibility considered (internal test harness) ## Update: readiness gating, reaped receipt, launcher receipts Follow-up commits on the same branch add: - fail_poll fires only after a readiness file proves the victim truly live under bounds (direct-exec sleeper, no orphan); readiness itself is asserted alongside the reaped receipt, cap readback, and elapsed bound. - BoundedRun gains a reaped receipt: OS termination accounted for, orthogonal to exit_code and to injected observation faults (which discard the observation, never the cleanup). reap_never asserts real cleanup ran alongside its discarded verdict. - Retained-pipe fixture runs under the fail-closed launcher; because incomplete pipe capture precludes the stderr readback by design, the effective stack/cpu bounds are proven through a per-process receipt file the fixture shell writes before parking the sleeper (removed afterwards).
What changed
Test-only, one file: threat_model_tests.rs. Closes the completion review on #2684 without touching #2681 distinct run_bounded_probe helper:
Before / After
Before: poll-fault break skipped termination; unconditional join after receipts; reap untestable without OS fakery; child poll panicked; selftests bypassed the launcher.
After: threat_model 222 passed (only pre-existing macOS nesting abort skipped, identical on unmodified code); error 14, partial_parse 11 green; fmt plus clippy -D warnings clean.
Risk
Checklist