[GH-ISSUE #4232] [Bug] LLDB denied launch loses observed exit and retains unknown target ownership #1664

Closed
opened 2026-10-16 21:08:44 +00:00 by bsakaguchi · 2 comments
Owner

Originally created by @morluto on GitHub (Oct 15, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/4232

Observed failure

A denied LLDB launch can return a valid, already-exited SBProcess. REA reports cleanup_incomplete with native-target:unknown and retains its owned temporary runtime instead of returning the actual capability denial after confirmed cleanup.

Related audit: #4227. This is distinct from #2397 (identity failure after a running launch) and the closed #3185 (pre-launch rejection). The bridge has a concrete exited process here, rather than no process or an unverified live process.

Reproduction and control

Revision: bde139c5d, REA 6.5.0, macOS 26.3, Node 24.18.0, Apple LLDB lldb-2100.0.17.108 / Swift 6.3.2.

  1. Compile the repository-owned tests/conformance/native/calls.m fixture with xcrun clang.
  2. Copy it to a disposable workspace and ad-hoc sign with hardened runtime and without get-task-allow.
  3. Invoke the public observe-native-calls CLI, or run npm run verify:native-calls.
  4. Observe the LLDB attach denial. Before repair, the verifier expects capability_unavailable but receives cleanup_incomplete; the CLI reports unknown target identity and retains an owned rea-lldb-* runtime.

An independent probe of the same owned fixture observed SBError.Success() == false, SBProcess.IsValid() == true, a nonzero process ID, and GetState() == 10 (eStateExited). The error begins process exited with status -1 (attach failed (Not allowed to attach to process...)).

Root cause

bridge/native/rea_lldb_tracer.py emits target_process_created: true for the valid failed-launch process but publishes its PID only on the success path. src/native/LldbCallTracer.ts correctly refuses to infer exit from a missing identity, so the lost producer facts become retained unknown ownership.

Object validity, historical creation, identity availability, and observed exit are separate facts. A launch error alone must not imply that no process survived; conversely, an exact owned process handle already in eStateExited should not become an unknown live target because launch failed.

Expected repair and validation

  • Publish observed PID and explicit exit state on failed launch paths, preserving historical process creation.
  • For a failed launch that remains live, retire only the exact owned process handle under the existing ownership protocol and positively observe exit; preserve uncertainty when proof is unavailable.
  • Consume explicit exit proof in the provider, rather than falsely changing creation to false or inferring liveness from IsValid().
  • Preserve capability denial and cleanup diagnostics through CLI and MCP.
  • Exercise valid-but-exited, valid-and-live, missing identity, and unknown-exit controls.

A local patch passes the real verify:native-calls CLI/stdio MCP lane, including positive observation, duration/event limits, denied hardened-runtime launch, and the get-task-allow control. Python bridge regressions pass; integration and full checks are still in progress. No released fix is claimed.

Upstream contract references

LLDB SBProcess exposes object validity, process ID and state separately. SBTarget Launch returns a process with an error output. Context7 and DeepWiki were consulted for these distinctions; the live probe above is the runtime evidence.

Originally created by @morluto on GitHub (Oct 15, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/4232 ### Observed failure A denied LLDB launch can return a valid, already-exited SBProcess. REA reports `cleanup_incomplete` with `native-target:unknown` and retains its owned temporary runtime instead of returning the actual capability denial after confirmed cleanup. Related audit: #4227. This is distinct from #2397 (identity failure after a running launch) and the closed #3185 (pre-launch rejection). The bridge has a concrete exited process here, rather than no process or an unverified live process. ### Reproduction and control Revision: `bde139c5d`, REA 6.5.0, macOS 26.3, Node 24.18.0, Apple LLDB `lldb-2100.0.17.108` / Swift 6.3.2. 1. Compile the repository-owned `tests/conformance/native/calls.m` fixture with `xcrun clang`. 2. Copy it to a disposable workspace and ad-hoc sign with hardened runtime and without get-task-allow. 3. Invoke the public `observe-native-calls` CLI, or run `npm run verify:native-calls`. 4. Observe the LLDB attach denial. Before repair, the verifier expects `capability_unavailable` but receives `cleanup_incomplete`; the CLI reports unknown target identity and retains an owned `rea-lldb-*` runtime. An independent probe of the same owned fixture observed `SBError.Success() == false`, `SBProcess.IsValid() == true`, a nonzero process ID, and `GetState() == 10` (`eStateExited`). The error begins `process exited with status -1 (attach failed (Not allowed to attach to process...))`. ### Root cause `bridge/native/rea_lldb_tracer.py` emits `target_process_created: true` for the valid failed-launch process but publishes its PID only on the success path. `src/native/LldbCallTracer.ts` correctly refuses to infer exit from a missing identity, so the lost producer facts become retained unknown ownership. Object validity, historical creation, identity availability, and observed exit are separate facts. A launch error alone must not imply that no process survived; conversely, an exact owned process handle already in `eStateExited` should not become an unknown live target because launch failed. ### Expected repair and validation - Publish observed PID and explicit exit state on failed launch paths, preserving historical process creation. - For a failed launch that remains live, retire only the exact owned process handle under the existing ownership protocol and positively observe exit; preserve uncertainty when proof is unavailable. - Consume explicit exit proof in the provider, rather than falsely changing creation to false or inferring liveness from `IsValid()`. - Preserve capability denial and cleanup diagnostics through CLI and MCP. - Exercise valid-but-exited, valid-and-live, missing identity, and unknown-exit controls. A local patch passes the real `verify:native-calls` CLI/stdio MCP lane, including positive observation, duration/event limits, denied hardened-runtime launch, and the get-task-allow control. Python bridge regressions pass; integration and full checks are still in progress. No released fix is claimed. ### Upstream contract references [LLDB SBProcess](https://lldb.llvm.org/python_reference/lldb.SBProcess-class.html) exposes object validity, process ID and state separately. [SBTarget Launch](https://lldb.llvm.org/python_reference/lldb.SBTarget-class.html) returns a process with an error output. Context7 and DeepWiki were consulted for these distinctions; the live probe above is the runtime evidence.
Author
Owner

@morluto commented on GitHub (Oct 15, 2026):

Local repair passed the real CLI/stdio MCP native-call verifier on Apple LLDB lldb-2100.0.17.108: normal exit, duration/event limits, denied hardened-runtime launch and the get-task-allow control. The bridge retains historical process creation, publishes the observed PID, and supplies explicit positive exit proof; the provider consumes that proof before attempting generic OS termination recovery. A failed Kill without an observed exit remains unresolved.

Bridge producer tests passed (13), including already-exited, live failed-launch retirement and failed-Kill/unknown-exit controls. LLDB boundary tests passed (10), including missing-identity negative controls and finalization cancellation. The combined lifecycle/schema regression run passed 114 tests across 14 files, and the full repository check passed. No released or globally installed fix is claimed: patches remain local and uncommitted.

<!-- gh-comment-id:6201491401 --> @morluto commented on GitHub (Oct 15, 2026): Local repair passed the real CLI/stdio MCP native-call verifier on Apple LLDB `lldb-2100.0.17.108`: normal exit, duration/event limits, denied hardened-runtime launch and the get-task-allow control. The bridge retains historical process creation, publishes the observed PID, and supplies explicit positive exit proof; the provider consumes that proof before attempting generic OS termination recovery. A failed Kill without an observed exit remains unresolved. Bridge producer tests passed (13), including already-exited, live failed-launch retirement and failed-Kill/unknown-exit controls. LLDB boundary tests passed (10), including missing-identity negative controls and finalization cancellation. The combined lifecycle/schema regression run passed 114 tests across 14 files, and the full repository check passed. No released or globally installed fix is claimed: patches remain local and uncommitted.
Author
Owner

@official-rex commented on GitHub (Oct 15, 2026):

I can review and finalize this fix. Kindly assign me this issue; I look forward to your response.

<!-- gh-comment-id:6202329587 --> @official-rex commented on GitHub (Oct 15, 2026): I can review and finalize this fix. Kindly assign me this issue; I look forward to your response.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#1664
No description provided.