[GH-ISSUE #4793] [Bug] macOS: a cold ownership-helper compile always exhausts the Ghidra Java probe's 5 s deadline #2492

Closed
opened 2026-10-17 21:05:39 +00:00 by bsakaguchi · 2 comments
Owner

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

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Provider or capability discovery

Observed behavior

On macOS with a cold Swift module cache, rea doctor --provider ghidra --json fails the ghidra-java check with <JAVA_HOME>/bin/java: Command deadline elapsed during ownership preparation. The JDK itself is fine. The 5 s Java probe deadline also bounds the one-time compile of the ownership helper (bridge/process/ProcessRunTokenReader*.swift), and a cold compile takes longer than 5 s. Each run aborts the compile before the cache warms, so every later run fails the same way. Ghidra stays unavailable until something else compiles the helper with a longer deadline.

I hit this after a Command Line Tools update left the default module cache cold. One manual xcrun swiftc of the two helper sources took 14.6 s and warmed the cache, and doctor passed after that.

Reproduction

On 78cf6100f (main) with macOS 26.6.2 arm64, Command Line Tools 27.0, Node 24.18.0, Ghidra 12.1.4 and OpenJDK 25.0.4.1.

To get a cold cache without clearing the default one, I pointed the documented REA_DARWIN_SWIFTC_PATH at a wrapper that runs the real compiler with an empty module cache:

cat > /tmp/cold-swiftc <<'EOF'
#!/bin/sh
exec /usr/bin/xcrun swiftc -module-cache-path "$REA_PROOF_MODULE_CACHE" "$@"
EOF
chmod 755 /tmp/cold-swiftc
mkdir /tmp/cold-module-cache

GHIDRA_INSTALL_DIR=/path/to/ghidra_12.1.4_PUBLIC \
JAVA_HOME=/path/to/jdk-25 \
REA_DARWIN_SWIFTC_PATH=/tmp/cold-swiftc \
SDKROOT=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk \
REA_PROOF_MODULE_CACHE=/tmp/cold-module-cache \
node scripts/rea.mjs doctor --provider ghidra --json

It failed 2 of 2 runs sharing one cache directory, and 2 of 2 earlier runs with the default cache after the Command Line Tools update.

Expected behavior

The helper compile has its own deadline, REA_SWIFTC_COMPILE_TIMEOUT_MS (default 120 s). The comment on resolveSwiftcCompileTimeoutMs says compilation "stays bounded independently of short provider version probes". PcapCommand, the Kuna commands and the source-map command call prepareProcessOwnershipInspection(signal) before their own deadlines start (#1137, #1139). The Java probe should do the same. Its first run takes as long as the compile needs, and the 5 s deadline covers only the java process.

Impact and scope

Observed: the Ghidra Java check fails on every run until the cache is warm, so the Ghidra provider is unavailable on a host with a cold cache. That happens after an Xcode or Command Line Tools update, on a fresh machine, or after a cache clear. It reproduced 4 of 4 times here.

Source-only, not reproduced: other runOwnedCommand callers that don't prepare first may hit the same limit on slow hosts. The apktool version probe allows 20 s, and the EVM, SQLite, WABT and web-capture decoders allow 30 s. #1137 measured an 18.5 s cold compile, and the comment on resolveSwiftcCompileTimeoutMs cites about 88 s on the Intel macOS VM.

REA version

78cf6100f (6.5.0 source)

Runtime and platform

macOS 26.6.2 arm64 (Apple M5 Max), Command Line Tools 27.0 (Swift 6.4), Node 24.18.0, Ghidra 12.1.4, Homebrew OpenJDK 25.0.4.1

Target and artifact context

No target. Only the Ghidra Java prerequisite.

Interface

CLI

Earliest failure stage

Setup or provider admission

Relevant output or evidence

Base 78cf6100f with a cold module cache, two runs sharing one cache directory:

run 1: 5.5s  ghidra-java ok=False  detail='<JAVA_HOME>/bin/java: Command deadline elapsed during ownership preparation.'  module cache 173 KiB
run 2: 5.5s  ghidra-java ok=False  detail='<JAVA_HOME>/bin/java: Command deadline elapsed during ownership preparation.'  module cache 173 KiB

The same compile run to completion takes 15 s and leaves an 89 MiB cache. A second compile against that cache takes 1 s.

Typed outcome or recovery expected

ghidra-java passes once the first, longer run finishes the compile. If the compile fails or exceeds REA_SWIFTC_COMPILE_TIMEOUT_MS, the probe still reports the ownership_preparation stage with the compiler outcome.

Analysis or suspected cause

probeGhidraJava in src/ghidra/GhidraJavaProbe.ts calls runOwnedCommand with timeoutMs: 5000. runOwnedCommand passes its deadline signal to spawnOwnedProviderProcess, which calls prepareProcessOwnershipInspection(options.signal), so the compile inherits the probe's 5 s deadline. runOwnedCommand includes preparation in its deadline on purpose (tests/boundary/process/ownedCommand.test.ts, "includes ownership preparation in the command deadline before provider creation"), and callers with short deadlines prepare first, as PcapCommand does. The Java probe doesn't. I have a fix that follows the PcapCommand pattern and will link the PR here.

Additional context

Related: #1137 and #1139 (the same failure in the source-map CLI), #2483 (the helper is compiled once per process), #4561 (Java probe lifecycle, source-only).

Originally created by @J0UH on GitHub (Oct 17, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/4793 ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Provider or capability discovery ### Observed behavior On macOS with a cold Swift module cache, `rea doctor --provider ghidra --json` fails the `ghidra-java` check with `<JAVA_HOME>/bin/java: Command deadline elapsed during ownership preparation.` The JDK itself is fine. The 5 s Java probe deadline also bounds the one-time compile of the ownership helper (`bridge/process/ProcessRunTokenReader*.swift`), and a cold compile takes longer than 5 s. Each run aborts the compile before the cache warms, so every later run fails the same way. Ghidra stays unavailable until something else compiles the helper with a longer deadline. I hit this after a Command Line Tools update left the default module cache cold. One manual `xcrun swiftc` of the two helper sources took 14.6 s and warmed the cache, and doctor passed after that. ### Reproduction On `78cf6100f` (main) with macOS 26.6.2 arm64, Command Line Tools 27.0, Node 24.18.0, Ghidra 12.1.4 and OpenJDK 25.0.4.1. To get a cold cache without clearing the default one, I pointed the documented `REA_DARWIN_SWIFTC_PATH` at a wrapper that runs the real compiler with an empty module cache: ```sh cat > /tmp/cold-swiftc <<'EOF' #!/bin/sh exec /usr/bin/xcrun swiftc -module-cache-path "$REA_PROOF_MODULE_CACHE" "$@" EOF chmod 755 /tmp/cold-swiftc mkdir /tmp/cold-module-cache GHIDRA_INSTALL_DIR=/path/to/ghidra_12.1.4_PUBLIC \ JAVA_HOME=/path/to/jdk-25 \ REA_DARWIN_SWIFTC_PATH=/tmp/cold-swiftc \ SDKROOT=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk \ REA_PROOF_MODULE_CACHE=/tmp/cold-module-cache \ node scripts/rea.mjs doctor --provider ghidra --json ``` It failed 2 of 2 runs sharing one cache directory, and 2 of 2 earlier runs with the default cache after the Command Line Tools update. ### Expected behavior The helper compile has its own deadline, `REA_SWIFTC_COMPILE_TIMEOUT_MS` (default 120 s). The comment on `resolveSwiftcCompileTimeoutMs` says compilation "stays bounded independently of short provider version probes". `PcapCommand`, the Kuna commands and the source-map command call `prepareProcessOwnershipInspection(signal)` before their own deadlines start (#1137, #1139). The Java probe should do the same. Its first run takes as long as the compile needs, and the 5 s deadline covers only the `java` process. ### Impact and scope Observed: the Ghidra Java check fails on every run until the cache is warm, so the Ghidra provider is unavailable on a host with a cold cache. That happens after an Xcode or Command Line Tools update, on a fresh machine, or after a cache clear. It reproduced 4 of 4 times here. Source-only, not reproduced: other `runOwnedCommand` callers that don't prepare first may hit the same limit on slow hosts. The apktool version probe allows 20 s, and the EVM, SQLite, WABT and web-capture decoders allow 30 s. #1137 measured an 18.5 s cold compile, and the comment on `resolveSwiftcCompileTimeoutMs` cites about 88 s on the Intel macOS VM. ### REA version `78cf6100f` (6.5.0 source) ### Runtime and platform macOS 26.6.2 arm64 (Apple M5 Max), Command Line Tools 27.0 (Swift 6.4), Node 24.18.0, Ghidra 12.1.4, Homebrew OpenJDK 25.0.4.1 ### Target and artifact context No target. Only the Ghidra Java prerequisite. ### Interface CLI ### Earliest failure stage Setup or provider admission ### Relevant output or evidence Base `78cf6100f` with a cold module cache, two runs sharing one cache directory: ```text run 1: 5.5s ghidra-java ok=False detail='<JAVA_HOME>/bin/java: Command deadline elapsed during ownership preparation.' module cache 173 KiB run 2: 5.5s ghidra-java ok=False detail='<JAVA_HOME>/bin/java: Command deadline elapsed during ownership preparation.' module cache 173 KiB ``` The same compile run to completion takes 15 s and leaves an 89 MiB cache. A second compile against that cache takes 1 s. ### Typed outcome or recovery expected `ghidra-java` passes once the first, longer run finishes the compile. If the compile fails or exceeds `REA_SWIFTC_COMPILE_TIMEOUT_MS`, the probe still reports the `ownership_preparation` stage with the compiler outcome. ### Analysis or suspected cause `probeGhidraJava` in `src/ghidra/GhidraJavaProbe.ts` calls `runOwnedCommand` with `timeoutMs: 5000`. `runOwnedCommand` passes its deadline signal to `spawnOwnedProviderProcess`, which calls `prepareProcessOwnershipInspection(options.signal)`, so the compile inherits the probe's 5 s deadline. `runOwnedCommand` includes preparation in its deadline on purpose (`tests/boundary/process/ownedCommand.test.ts`, "includes ownership preparation in the command deadline before provider creation"), and callers with short deadlines prepare first, as `PcapCommand` does. The Java probe doesn't. I have a fix that follows the `PcapCommand` pattern and will link the PR here. ### Additional context Related: #1137 and #1139 (the same failure in the source-map CLI), #2483 (the helper is compiled once per process), #4561 (Java probe lifecycle, source-only).
Author
Owner

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

Fix in #4795 (draft until the human owner completes the readiness declarations).

<!-- gh-comment-id:6234081470 --> @J0UH commented on GitHub (Oct 17, 2026): Fix in #4795 (draft until the human owner completes the readiness declarations).
Author
Owner

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

Fixed on main in 376eeca77, adapting @J0UH's PR #4795 with coauthor credit.

The Java probe now prepares independently bounded macOS ownership inspection before starting its five-second command deadline. Linux preparation stays inside command supervision because its process-table check has no separate deadline. Shared command code owns preparation-failure translation for both stages; the probe no longer needs a duplicated private mapper. Physical compiler outcomes, opaque cancellation reasons and exact helper cleanup capabilities retain their existing semantics.

Validation executed: the contributor's controlled cold-preparation regression failed on baseline f4322b15e. After the fix, 114 checks across nine files passed, with one native Linux-only skip. The final preparation/owner rerun passed 13 checks with the same skip. Coverage includes preparation failure, before/during cancellation, simulated Linux deadline, cancellation at the completed preparation handoff, helper cleanup denial/retry, existing command deadlines, real process-family cleanup, and public doctor delivery. Build, typecheck/lint, docs (five README checks), commit/push hooks passed. Two read-only reviews found no blockers.

18 completed before/after cost tasks covered single and three sequential probes plus actual public doctor --provider ghidra --json. All replies were byte-identical. Repeated-process medians baseline/candidate: single probe 0.870/0.867s elapsed, 0.704/0.704s CPU, 234.7/234.2 MB sampled family peak, 69/69 estimated reply tokens; doctor 1.251/1.335s elapsed, 1.276/1.306s CPU, 529.1/525.7 MB family peak, 2758/2758 tokens. Two alternating repeats and separate first processes; no statistical speed or true-cold claim. Whole-agent, MCP/catalog and actual cold-compile costs remain unmeasured.

The regression delays the preparation boundary around a prepared real inspector. Java-shaped Node fixtures exercise owned process handling; no actual cold Swift compilation, real JVM/Ghidra, investigated target, or heavy profiler/stress run was performed. Native Linux behavior remains unverified on this macOS host.

The other short-deadline callers mentioned in #4793 remain source-audit candidates. Their contracts need individual review before moving preparation: runOwnedCommand intentionally continues to include launch preparation in its command deadline. This fix establishes the explicit preparation path for the Java prerequisite without changing that general contract.

<!-- gh-comment-id:6234863018 --> @morluto commented on GitHub (Oct 17, 2026): Fixed on main in 376eeca77, adapting @J0UH's PR #4795 with coauthor credit. The Java probe now prepares independently bounded macOS ownership inspection before starting its five-second command deadline. Linux preparation stays inside command supervision because its process-table check has no separate deadline. Shared command code owns preparation-failure translation for both stages; the probe no longer needs a duplicated private mapper. Physical compiler outcomes, opaque cancellation reasons and exact helper cleanup capabilities retain their existing semantics. Validation executed: the contributor's controlled cold-preparation regression failed on baseline f4322b15e. After the fix, 114 checks across nine files passed, with one native Linux-only skip. The final preparation/owner rerun passed 13 checks with the same skip. Coverage includes preparation failure, before/during cancellation, simulated Linux deadline, cancellation at the completed preparation handoff, helper cleanup denial/retry, existing command deadlines, real process-family cleanup, and public doctor delivery. Build, typecheck/lint, docs (five README checks), commit/push hooks passed. Two read-only reviews found no blockers. 18 completed before/after cost tasks covered single and three sequential probes plus actual public `doctor --provider ghidra --json`. All replies were byte-identical. Repeated-process medians baseline/candidate: single probe 0.870/0.867s elapsed, 0.704/0.704s CPU, 234.7/234.2 MB sampled family peak, 69/69 estimated reply tokens; doctor 1.251/1.335s elapsed, 1.276/1.306s CPU, 529.1/525.7 MB family peak, 2758/2758 tokens. Two alternating repeats and separate first processes; no statistical speed or true-cold claim. Whole-agent, MCP/catalog and actual cold-compile costs remain unmeasured. The regression delays the preparation boundary around a prepared real inspector. Java-shaped Node fixtures exercise owned process handling; no actual cold Swift compilation, real JVM/Ghidra, investigated target, or heavy profiler/stress run was performed. Native Linux behavior remains unverified on this macOS host. The other short-deadline callers mentioned in #4793 remain source-audit candidates. Their contracts need individual review before moving preparation: `runOwnedCommand` intentionally continues to include launch preparation in its command deadline. This fix establishes the explicit preparation path for the Java prerequisite without changing that general contract.
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#2492
No description provided.