--- id: "bug-1808" status: "fixed" status_updated: "2026-09-24" status_note: "" component: "harness" severity: "leak" timestamp: "2026-09-14T15:50:00Z" last_seen: "2026-09-19T09:00:00Z" occurrences: 1 files: ["src/harness/testsuite/test_test_cli.c", "src/harness/testsuite/harness_test.c"] tags: ["process-leak", "fd-leak", "unity", "longjmp", "test-infrastructure", "test_test_cli.c", "clitest"] related: ["bug-1807"] legacy: true --- # bug-1808: src/harness/testsuite/test_test_cli.c: a spawned xce_test child process… ## Description src/harness/testsuite/test_test_cli.c: a spawned xce_test child process (clitest_spawn/clitest_spawn_ex) plus its pty master fd and stderr pipe fd leak whenever a TEST_ASSERT_* assertion fails partway through the owning test function, because every test relies on its OWN clitest_kill_and_reap()+clitest_close() calls placed inline at the bottom of the function body -- code that Unity's longjmp-based TEST_PROTECT()/TEST_ABORT() abort mechanism skips entirely on assertion failure. Directly observed: 4 live, idle `xce_test --norc` child processes still running (blocked forever on their own prompt) under one in-progress harness_unit_test invocation, discovered while investigating bug-1807's failures. ## Environment _Not recorded (migrated from buglog.json)._ ## Reproduction _Not recorded (migrated from buglog.json)._ ## Root cause Design gap, not a logic error in any specific test: clitest_kill_and_reap()/clitest_close() were documented as a mandatory safety net ('every caller is required to always end a session with clitest_kill_and_reap() regardless of which path was taken') but placed only at each test function's own normal-exit code path. Unity's RUN_TEST() macro DOES guarantee tearDown() runs after every test (pass, fail, or aborted-via-longjmp), but harness_test.c's shared tearDown() (used by all 13 Unity test groups linked into this binary, most unrelated to clitest) was a no-op beyond a cosmetic separator print -- it never reaped a still-open clitest session. ## Fix FIXED. Added a static clitest_session_t mirror (g_last_session) in test_test_cli.c updated by clitest_spawn_ex() (set on successful spawn), clitest_kill_and_reap() (reaped flag synced), and clitest_close() (cleared) -- a STATIC-storage mirror was required rather than tracking a pointer into the aborted test's own local `sess`, since that stack frame is unwound by the longjmp and would leave a dangling pointer. Exposed a new clitest_reap_leaked_session() function, called from harness_test.c's tearDown() (now declared extern there), which force-kills+reaps+closes any session left open when a test aborted before reaching its own inline cleanup. Only one clitest session is ever open at a time in this file (verified), so a single mirror slot is sufficient. Verified via ASAN build (xce.build): no LeakSanitizer or other sanitizer findings from the new code across a clean 5/5 harness suite run. ## Verification _Not recorded (migrated from buglog.json)._ ## References - Related: bug-1807