[GH-ISSUE #4506] [Bug] Ghidra request cancellation can report success or settle before cleanup #1790

Closed
opened 2026-10-16 21:10:16 +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/4506

Before submitting

  • Searched open and closed Ghidra request/cancellation reports. #3983 concerns shared startup wait ownership, #3977 DOS snapshot attestation, and #4502 import descriptor creation; these are distinct.
  • No credentials or sensitive target data included.

Area

Native analysis or decompilation

Observed behavior

Source-confirmed Ghidra request lifecycle defects. GhidraRequestQueue constructs cancellation after an executor success, but returns that success if cancellation cleanup succeeds. Its separate executor rejection catch returns before active cancellation cleanup settles; cleanup rejection is handled only after executor fulfillment and can be unhandled in the meantime. Unexpected cleanup rejection omits cleanupFailure. GhidraClient replaces already observed process/protocol failures with cancellation when the caller aborts during diagnostic drain. Queue and wire cancellation omit the caller's exact reason.

Reproduction

NOT_ENOUGH_INFO for historical native-engine reproduction. Proposed controlled production-boundary interleavings: resolve executor success then abort before its continuation, gate cleanup; reject executor during active cleanup; reject cleanup while executor is still pending; abort during output drain after a process/protocol failure; queued/pre-aborted/in-flight null/string/Error reasons. No new runtime fault reproduction claimed at filing.

Expected behavior

One completion owner joins every admitted execution and cleanup outcome before settling. Existing primary failures remain primary, cancellation never reports success after destroying its session, unexpected cleanup rejection remains unconfirmed cleanup, and exact caller reasons survive transport/public translation.

Impact and scope

Shared Ghidra CLI/MCP request queue and failure finalization. Incorrect success, early settlement, unhandled rejection, misleading cancellation or missing cleanup uncertainty are possible from the inspected control flow. Natural frequency and native provider faults unknown. No new authority or target execution proposed.

REA version

6.5.0; source base fe2a398dd9 with ongoing local audit changes.

Runtime and platform

Source analysis on macOS arm64, Node24.18.0. Native Ghidra fault reproduction not performed.

Target and artifact context

Digest-committed target copy into the ephemeral private Ghidra import root; source bytes are read-only.

Interface

Shared Ghidra startup used by CLI/MCP.

Earliest failure stage

Cleanup, cancellation, or lifecycle

Relevant output or evidence

src/ghidra/GhidraRequestQueue.ts drain: result.ok constructs primary cancellation but result changes only on cleanup failure; separate .catch bypasses awaiting activeCancellation. onAbort assigns a potentially rejected promise before any rejection handler. withUnexpectedCleanupRejection omits cleanupFailure. src/ghidra/GhidraClient.ts request executor awaits drain then replaces result.error on latest signal. src/ghidra/GhidraClientWire.ts cancelledValue and queue cancellation factory calls omit reason metadata.

Typed outcome or recovery expected

Preserve execution Result, exact cancellation fact and separate confirmed/unconfirmed cleanup. Await finalization even after execution rejects. Only the resource owner can later confirm release. A later signal cannot reinterpret an earlier process/protocol failure.

Analysis or suspected cause

Separate promise fulfillment/rejection/finalization branches and downstream signal guessing duplicate lifecycle interpretation. Consolidate queue completion; consume typed producer outcomes and the existing GhidraSessionError cancellation representation.

Additional context

Adjacent #4502 uses the same primary-outcome invariant at snapshot creation. No natural/native failure reproduction or measurements claimed at filing. Native Windows ownership4505 remains a separate tracked concern.

Originally created by @morluto on GitHub (Oct 16, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/4506 ### Before submitting - [x] Searched open and closed Ghidra request/cancellation reports. #3983 concerns shared startup wait ownership, #3977 DOS snapshot attestation, and #4502 import descriptor creation; these are distinct. - [x] No credentials or sensitive target data included. ### Area Native analysis or decompilation ### Observed behavior Source-confirmed Ghidra request lifecycle defects. GhidraRequestQueue constructs cancellation after an executor success, but returns that success if cancellation cleanup succeeds. Its separate executor rejection catch returns before active cancellation cleanup settles; cleanup rejection is handled only after executor fulfillment and can be unhandled in the meantime. Unexpected cleanup rejection omits cleanupFailure. GhidraClient replaces already observed process/protocol failures with cancellation when the caller aborts during diagnostic drain. Queue and wire cancellation omit the caller's exact reason. ### Reproduction NOT_ENOUGH_INFO for historical native-engine reproduction. Proposed controlled production-boundary interleavings: resolve executor success then abort before its continuation, gate cleanup; reject executor during active cleanup; reject cleanup while executor is still pending; abort during output drain after a process/protocol failure; queued/pre-aborted/in-flight null/string/Error reasons. No new runtime fault reproduction claimed at filing. ### Expected behavior One completion owner joins every admitted execution and cleanup outcome before settling. Existing primary failures remain primary, cancellation never reports success after destroying its session, unexpected cleanup rejection remains unconfirmed cleanup, and exact caller reasons survive transport/public translation. ### Impact and scope Shared Ghidra CLI/MCP request queue and failure finalization. Incorrect success, early settlement, unhandled rejection, misleading cancellation or missing cleanup uncertainty are possible from the inspected control flow. Natural frequency and native provider faults unknown. No new authority or target execution proposed. ### REA version 6.5.0; source base fe2a398dd9ab290ba6cb3411403d319cec8d07a2 with ongoing local audit changes. ### Runtime and platform Source analysis on macOS arm64, Node24.18.0. Native Ghidra fault reproduction not performed. ### Target and artifact context Digest-committed target copy into the ephemeral private Ghidra import root; source bytes are read-only. ### Interface Shared Ghidra startup used by CLI/MCP. ### Earliest failure stage Cleanup, cancellation, or lifecycle ### Relevant output or evidence src/ghidra/GhidraRequestQueue.ts drain: result.ok constructs primary cancellation but result changes only on cleanup failure; separate .catch bypasses awaiting activeCancellation. onAbort assigns a potentially rejected promise before any rejection handler. withUnexpectedCleanupRejection omits cleanupFailure. src/ghidra/GhidraClient.ts request executor awaits drain then replaces result.error on latest signal. src/ghidra/GhidraClientWire.ts cancelledValue and queue cancellation factory calls omit reason metadata. ### Typed outcome or recovery expected Preserve execution Result, exact cancellation fact and separate confirmed/unconfirmed cleanup. Await finalization even after execution rejects. Only the resource owner can later confirm release. A later signal cannot reinterpret an earlier process/protocol failure. ### Analysis or suspected cause Separate promise fulfillment/rejection/finalization branches and downstream signal guessing duplicate lifecycle interpretation. Consolidate queue completion; consume typed producer outcomes and the existing GhidraSessionError cancellation representation. ### Additional context Adjacent #4502 uses the same primary-outcome invariant at snapshot creation. No natural/native failure reproduction or measurements claimed at filing. Native Windows ownership4505 remains a separate tracked concern.
Author
Owner

@kaoru0822-kitauji commented on GitHub (Oct 17, 2026):

This appears fixed on current main (2092f90b7) by 9d940c2f2 (the commit message references this issue).

Executed on main 2092f90b7 (macOS arm64, Node 24): npm run test:focused -- … including src/ghidra/GhidraRequestQueue.test.ts (25 tests) — all passed (205/205 across the 13 files checked in this batch). Source check: GhidraRequestQueue.#executeEntry now awaits cancellation.cleanup on both fulfilled and rejected execution paths and attaches a ProviderCleanupError for unexpected cleanup rejection.

I did not rerun the original fault injection from the report separately; the tests above are the regressions added with the fix. Leaving closure to a maintainer.

<!-- gh-comment-id:6232499070 --> @kaoru0822-kitauji commented on GitHub (Oct 17, 2026): This appears fixed on current main (2092f90b7) by 9d940c2f2 (the commit message references this issue). Executed on main 2092f90b7 (macOS arm64, Node 24): `npm run test:focused -- …` including `src/ghidra/GhidraRequestQueue.test.ts` (25 tests) — all passed (205/205 across the 13 files checked in this batch). Source check: `GhidraRequestQueue.#executeEntry` now awaits `cancellation.cleanup` on both fulfilled and rejected execution paths and attaches a `ProviderCleanupError` for unexpected cleanup rejection. I did not rerun the original fault injection from the report separately; the tests above are the regressions added with the fix. Leaving closure to a maintainer.
Author
Owner

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

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

  • 9d940c2f2: fix(ghidra): preserve lifecycle ownership and failure provenance. Retain snapshot, endpoint, launch rollback and compiler cleanup owners. Join request finalization and cleanup cancellation. Preserve bounded compiler output and original byte counts through capture and Ghidra failures. Normalize execFile cancellation once at its producer.

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:6256011468 --> @morluto commented on GitHub (Oct 18, 2026): <!-- rea-backlog-reconciliation-2026-10-19-4506 --> Closing as fixed on `main` after reconciling this report with the landed owner/consumer changes. - [9d940c2f2](https://github.com/morluto/rea/commit/9d940c2f284cc6f75b27900336e52ceeba4aa2af): fix(ghidra): preserve lifecycle ownership and failure provenance. Retain snapshot, endpoint, launch rollback and compiler cleanup owners. Join request finalization and cleanup cancellation. Preserve bounded compiler output and original byte counts through capture and Ghidra failures. Normalize execFile cancellation once at its producer. 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#1790
No description provided.