[GH-ISSUE #4508] [Bug] Ghidra TCP endpoint observation can block on a pipe and lose cleanup ownership #1791

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/4508

Before submitting

  • Searched open and closed Ghidra endpoint/read/cleanup reports. #4502 is import snapshot creation, #3977 DOS attestation, and #4471 Cutter descriptor admission; these are distinct endpoint consumers.
  • No credentials or sensitive target data included.

Area

Native analysis or decompilation

Observed behavior

The compiled POSIX observeGhidraEndpoint TCP-descriptor path blocks on a named pipe: an owned FIFO probe remained pending for the full2-second observation window and completed only after an owned writer connected and closed. Source also confirms that raw finally handle.close can replace a successful/invalid endpoint Result and has no retained failed-descriptor retry owner. connectGhidraSocket does not pass its startup signal into descriptor observation.

Reproduction

Executed production compiled module with a private mkfifo endpoint and no writer, in an owned Node24.18.0 child. observeGhidraEndpoint({transport:"authenticated-loopback-tcp",path: fifo}) did not settle within2seconds. Connecting/closing an owned writer unblocked it; it returned typed invalid-endpoint failure and exited0. FIFO, child and parent were cleaned. No Ghidra engine or CLI/MCP run; startup cancellation and failed-close effects remain source-derived, not fault-injected here.

Expected behavior

Canonical nonblocking regular-file admission rejects special-file endpoints without waiting. The observer owns read/cancellation/close as one outcome, retains exact rejected close owners, and integrates observation lifetime and cleanup with startup/client ownership. Existing failure classifications must survive later cancellation and close failure.

Impact and scope

POSIX authenticated-loopback-TCP endpoint discovery. A malformed/special endpoint can stall startup observation beyond the intended deadline; cleanup can discard primary results or failed ownership. This is a local owned fixture, not a natural malicious/native-engine incident. Windows native runtime has a separate owner and #4505 tracks its handle-close problem.

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/GhidraTransport.ts observes POSIX TCP descriptor with open(path,"r"), handle.readFile("utf8"), finally await handle.close(). src/ghidra/GhidraSocketConnection.ts calls observeGhidraEndpoint(options.endpoint) without its deadline signal. Executed compiled probe: pending through2seconds, then typed start/invalid-endpoint failure after FIFO writer release; child exit0. No leak count claimed.

Typed outcome or recovery expected

Reject nonregular endpoint inputs promptly; preserve exact cancellation reason and existing IO/parse failures. Retain exact failed-close capabilities and await observation finalization before root removal. Runtime-tree cleanup alone does not establish descriptor release.

Analysis or suspected cause

Endpoint reader bypasses existing shared nonblocking regular-file and lifecycle owners. Producer read and close are separate finally stages, while deadline authority lives only in the downstream polling loop.

Additional context

Adjacent to source lifecycle audit #4502/#4506. No new authority, target execution or mutation proposed. Unbounded readFile allocation is an additional source resource concern; any limit must follow the actual bridge publication/protocol format rather than an arbitrary fixture ceiling.

Originally created by @morluto on GitHub (Oct 16, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/4508 ### Before submitting - [x] Searched open and closed Ghidra endpoint/read/cleanup reports. #4502 is import snapshot creation, #3977 DOS attestation, and #4471 Cutter descriptor admission; these are distinct endpoint consumers. - [x] No credentials or sensitive target data included. ### Area Native analysis or decompilation ### Observed behavior The compiled POSIX observeGhidraEndpoint TCP-descriptor path blocks on a named pipe: an owned FIFO probe remained pending for the full2-second observation window and completed only after an owned writer connected and closed. Source also confirms that raw finally handle.close can replace a successful/invalid endpoint Result and has no retained failed-descriptor retry owner. connectGhidraSocket does not pass its startup signal into descriptor observation. ### Reproduction Executed production compiled module with a private mkfifo endpoint and no writer, in an owned Node24.18.0 child. observeGhidraEndpoint({transport:"authenticated-loopback-tcp",path: fifo}) did not settle within2seconds. Connecting/closing an owned writer unblocked it; it returned typed invalid-endpoint failure and exited0. FIFO, child and parent were cleaned. No Ghidra engine or CLI/MCP run; startup cancellation and failed-close effects remain source-derived, not fault-injected here. ### Expected behavior Canonical nonblocking regular-file admission rejects special-file endpoints without waiting. The observer owns read/cancellation/close as one outcome, retains exact rejected close owners, and integrates observation lifetime and cleanup with startup/client ownership. Existing failure classifications must survive later cancellation and close failure. ### Impact and scope POSIX authenticated-loopback-TCP endpoint discovery. A malformed/special endpoint can stall startup observation beyond the intended deadline; cleanup can discard primary results or failed ownership. This is a local owned fixture, not a natural malicious/native-engine incident. Windows native runtime has a separate owner and #4505 tracks its handle-close problem. ### 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/GhidraTransport.ts observes POSIX TCP descriptor with open(path,"r"), handle.readFile("utf8"), finally await handle.close(). src/ghidra/GhidraSocketConnection.ts calls observeGhidraEndpoint(options.endpoint) without its deadline signal. Executed compiled probe: pending through2seconds, then typed start/invalid-endpoint failure after FIFO writer release; child exit0. No leak count claimed. ### Typed outcome or recovery expected Reject nonregular endpoint inputs promptly; preserve exact cancellation reason and existing IO/parse failures. Retain exact failed-close capabilities and await observation finalization before root removal. Runtime-tree cleanup alone does not establish descriptor release. ### Analysis or suspected cause Endpoint reader bypasses existing shared nonblocking regular-file and lifecycle owners. Producer read and close are separate finally stages, while deadline authority lives only in the downstream polling loop. ### Additional context Adjacent to source lifecycle audit #4502/#4506. No new authority, target execution or mutation proposed. Unbounded readFile allocation is an additional source resource concern; any limit must follow the actual bridge publication/protocol format rather than an arbitrary fixture ceiling.
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 tests/process-global/ghidraEndpointLifetime.test.ts (14 tests) — all passed (205/205 across the 13 files checked in this batch). Source check: observeGhidraEndpoint now reads the POSIX TCP descriptor through withArtifactRegularFile with the caller's signal and resource scope.

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:6232498648 --> @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 `tests/process-global/ghidraEndpointLifetime.test.ts` (14 tests) — all passed (205/205 across the 13 files checked in this batch). Source check: `observeGhidraEndpoint` now reads the POSIX TCP descriptor through `withArtifactRegularFile` with the caller's signal and resource scope. 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:6256010669 --> @morluto commented on GitHub (Oct 18, 2026): <!-- rea-backlog-reconciliation-2026-10-19-4508 --> 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#1791
No description provided.