[GH-ISSUE #4517] [Bug] Ghidra launcher replaces earlier startup failures with later cancellation #1795

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

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

Before submitting

  • Searched open issues, closed Ghidra launch/cancellation/rollback reports, and open PR bodies for overlap.
  • Removed credentials and private target data.

Area

Native analysis or decompilation

Observed behavior

Source-only finding. GhidraHeadlessLauncher.launch() converts any caught error into cancellation whenever the signal is now aborted (src/ghidra/GhidraLauncher.ts, catch after ownership-manifest publication). GhidraClient repeats this conversion for every failed launcher result. An earlier filesystem or LaunchSupport failure can therefore be replaced by a later cancellation/timeout. Explicit launcher cancellation checks also omit the caller's exact reason.

Reproduction

NOT_ENOUGH_INFO: no native-engine reproduction performed. Inspected the producing catch and consuming failed-launch branch at the revision below. Proposed regression: reject a launch command or manifest write with a known error while aborting; verify the physical failure remains primary. Separately check null/string/Error cancellation reasons.

Expected behavior

Preserve the producer's typed primary outcome; normalize cancellation at its actual producing boundary and retain the exact reason. Later cancellation during cleanup must not replace earlier failure (AGENTS.md lifecycle invariants).

Impact and scope

Source-derived diagnostic/cancellation correctness defect in shared CLI/MCP Ghidra startup. Runtime frequency and native-engine fault incidence: NOT_ENOUGH_INFO. Separate from #3977 (DOS snapshot verification), #4506 (queue/handshake), and #4508 (endpoint descriptor lifetime).

REA version

Source base fe2a398dd9; local audit changes also inspected.

Runtime and platform

Source review on macOS arm64, Node 24.18.0; Windows/native-engine fault behavior unverified.

Target and artifact context

No private target; launcher startup control flow.

Interface

Other (shared CLI/MCP provider boundary)

Earliest failure stage

Setup or provider admission

Evidence

src/ghidra/GhidraLauncher.ts catch selects primary from latest isAborted(options.signal); src/ghidra/GhidraClient.ts failed launcher branch selects from deadline.signal.aborted. No execution claim.

Originally created by @morluto on GitHub (Oct 16, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/4517 ### Before submitting - [x] Searched open issues, closed Ghidra launch/cancellation/rollback reports, and open PR bodies for overlap. - [x] Removed credentials and private target data. ### Area Native analysis or decompilation ### Observed behavior **Source-only finding.** `GhidraHeadlessLauncher.launch()` converts any caught error into cancellation whenever the signal is now aborted (`src/ghidra/GhidraLauncher.ts`, catch after ownership-manifest publication). `GhidraClient` repeats this conversion for every failed launcher result. An earlier filesystem or LaunchSupport failure can therefore be replaced by a later cancellation/timeout. Explicit launcher cancellation checks also omit the caller's exact reason. ### Reproduction NOT_ENOUGH_INFO: no native-engine reproduction performed. Inspected the producing catch and consuming failed-launch branch at the revision below. Proposed regression: reject a launch command or manifest write with a known error while aborting; verify the physical failure remains primary. Separately check null/string/Error cancellation reasons. ### Expected behavior Preserve the producer's typed primary outcome; normalize cancellation at its actual producing boundary and retain the exact reason. Later cancellation during cleanup must not replace earlier failure (AGENTS.md lifecycle invariants). ### Impact and scope Source-derived diagnostic/cancellation correctness defect in shared CLI/MCP Ghidra startup. Runtime frequency and native-engine fault incidence: NOT_ENOUGH_INFO. Separate from #3977 (DOS snapshot verification), #4506 (queue/handshake), and #4508 (endpoint descriptor lifetime). ### REA version Source base fe2a398dd9ab290ba6cb3411403d319cec8d07a2; local audit changes also inspected. ### Runtime and platform Source review on macOS arm64, Node 24.18.0; Windows/native-engine fault behavior unverified. ### Target and artifact context No private target; launcher startup control flow. ### Interface Other (shared CLI/MCP provider boundary) ### Earliest failure stage Setup or provider admission ### Evidence `src/ghidra/GhidraLauncher.ts` catch selects primary from latest `isAborted(options.signal)`; `src/ghidra/GhidraClient.ts` failed launcher branch selects from `deadline.signal.aborted`. No execution claim.
Author
Owner

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

Published on main in 1e36e707b (fix(ghidra): preserve launch outcomes through cancellation and rollback).

Controlled reproduction against e31aa72c9: five launcher controls failed (physical/LaunchSupport failure overwritten by later abort, and missing null/string/Error helper cancellation facts). A synchronized client control also failed: a known failed launcher result held until the final caller's cancellation reached startup returned cancelled instead of its physical start failure. These controls now pass. No real Ghidra/JVM fault reproduction is claimed.

Structural fix: the launch diagnostic envelope carries its authored primary cancellation independently of its diagnostic cause. Physical errors are no longer classified from the current signal. Spawn preparation's exact raw abort reason is wrapped before typed-error interpretation, preserving opaque caller reasons without adopting their resources. Checkpoints retain exact reasons. Rollback explicitly carries the primary fact through its aggregate and retains the original process owner; the client consumes that fact rather than resampling a failed launch's deadline. LaunchSupport warnings remain on the launch envelope, including cancellation and timeout.

Validation executed:

  • 185 tests across client, provider, launch outcome, and rollback files.
  • 40 tests across LaunchSupport and MCP startup/failure files; overlaps the seven launch-outcome tests above.
  • 22 launcher tests passed, three platform tests skipped.
  • npm run check:fast, npm run docs:check (five conformance tests), normal commit/push hooks.

Controls include failed manifest publication, later cancellation during rollback, incomplete and synchronously rejected rollback, exact cleanup retry, cancellation after process creation with null/string reasons, producer cancellation without an aborted client deadline, timeout while a physical result finalizes, opaque typed caller reasons, and preservation of helper warnings through provider-neutral projections. Process fixtures use idle Node children and verified production ownership; MCP fixtures use the controlled Node bridge.

Completed task comparison against clean e31aa72c9: actual headless launcher, owned process, authenticated client startup, list_strings, and close with a synthetic Node bridge. Two alternating warm filesystem repeats: baseline/candidate elapsed 0.836/0.839s, CPU 0.648/0.646s, sampled process-family RSS 261.94/262.11MB, complete reply cl100k_base tokens 48/48. Separate first processes recorded; caches not flushed. CPU uses wait4 root plus reaped children; sampled RSS can miss transients/reparented children. Whole-agent/catalog/retry, public CLI/MCP overhead, failure-path costs and real-engine performance remain unmeasured. No performance improvement claim or heavy profiling workload.

Scope: controlled Darwin arm64/Node 24.18.0 boundaries, actual process ownership, and specified CLI/provider/MCP projections. Real Ghidra/JVM and Windows/Linux native interruption/fault behavior remain unverified.

<!-- gh-comment-id:6229631586 --> @morluto commented on GitHub (Oct 17, 2026): Published on main in 1e36e707b (`fix(ghidra): preserve launch outcomes through cancellation and rollback`). Controlled reproduction against e31aa72c9: five launcher controls failed (physical/LaunchSupport failure overwritten by later abort, and missing null/string/Error helper cancellation facts). A synchronized client control also failed: a known failed launcher result held until the final caller's cancellation reached startup returned `cancelled` instead of its physical `start` failure. These controls now pass. No real Ghidra/JVM fault reproduction is claimed. Structural fix: the launch diagnostic envelope carries its authored primary cancellation independently of its diagnostic cause. Physical errors are no longer classified from the current signal. Spawn preparation's exact raw abort reason is wrapped before typed-error interpretation, preserving opaque caller reasons without adopting their resources. Checkpoints retain exact reasons. Rollback explicitly carries the primary fact through its aggregate and retains the original process owner; the client consumes that fact rather than resampling a failed launch's deadline. LaunchSupport warnings remain on the launch envelope, including cancellation and timeout. Validation executed: - 185 tests across client, provider, launch outcome, and rollback files. - 40 tests across LaunchSupport and MCP startup/failure files; overlaps the seven launch-outcome tests above. - 22 launcher tests passed, three platform tests skipped. - `npm run check:fast`, `npm run docs:check` (five conformance tests), normal commit/push hooks. Controls include failed manifest publication, later cancellation during rollback, incomplete and synchronously rejected rollback, exact cleanup retry, cancellation after process creation with null/string reasons, producer cancellation without an aborted client deadline, timeout while a physical result finalizes, opaque typed caller reasons, and preservation of helper warnings through provider-neutral projections. Process fixtures use idle Node children and verified production ownership; MCP fixtures use the controlled Node bridge. Completed task comparison against clean e31aa72c9: actual headless launcher, owned process, authenticated client startup, `list_strings`, and close with a synthetic Node bridge. Two alternating warm filesystem repeats: baseline/candidate elapsed 0.836/0.839s, CPU 0.648/0.646s, sampled process-family RSS 261.94/262.11MB, complete reply cl100k_base tokens 48/48. Separate first processes recorded; caches not flushed. CPU uses wait4 root plus reaped children; sampled RSS can miss transients/reparented children. Whole-agent/catalog/retry, public CLI/MCP overhead, failure-path costs and real-engine performance remain unmeasured. No performance improvement claim or heavy profiling workload. Scope: controlled Darwin arm64/Node 24.18.0 boundaries, actual process ownership, and specified CLI/provider/MCP projections. Real Ghidra/JVM and Windows/Linux native interruption/fault behavior remain unverified.
Author
Owner

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

Closing as fixed on main after reconciling this report with the landed owner/consumer changes.

  • 1e36e707b: fix(ghidra): preserve launch outcomes through cancellation and rollback. Validation: focused client/provider/rollback and helper/MCP tests, check:fast, docs:check. Controlled native process ownership and Node bridge only; real Ghidra/JVM and Windows/Linux fault behavior unverified.
  • b0ddb58e2: fix(ghidra): own PSP inspection finalization. Validation: 137 tests passed, 3 platform skips across six filesystem/provider/MCP files; check:fast passed. Controlled real-handle fixtures do not establish actual Allegrex execution or native Windows faults.

Evidence scope: this closure uses the landed source, regression coverage and validation already recorded in the linked commits/thread. No new local tests or native-provider reproduction were run during this backlog pass; previously documented platform and real-provider gaps still apply.

<!-- gh-comment-id:6256010016 --> @morluto commented on GitHub (Oct 18, 2026): <!-- rea-backlog-reconciliation-2026-10-19-4517 --> Closing as fixed on `main` after reconciling this report with the landed owner/consumer changes. - [1e36e707b](https://github.com/morluto/rea/commit/1e36e707b1f991fde826947bab04d436e64b7f97): fix(ghidra): preserve launch outcomes through cancellation and rollback. Validation: focused client/provider/rollback and helper/MCP tests, check:fast, docs:check. Controlled native process ownership and Node bridge only; real Ghidra/JVM and Windows/Linux fault behavior unverified. - [b0ddb58e2](https://github.com/morluto/rea/commit/b0ddb58e27b18af996f0411b0eb02a128f237a0a): fix(ghidra): own PSP inspection finalization. Validation: 137 tests passed, 3 platform skips across six filesystem/provider/MCP files; check:fast passed. Controlled real-handle fixtures do not establish actual Allegrex execution or native Windows faults. Evidence scope: this closure uses the landed source, regression coverage and validation already recorded in the linked commits/thread. No new local tests or native-provider reproduction were run during this backlog pass; previously documented platform and real-provider gaps still apply.
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#1795
No description provided.