[PR #4795] [CLOSED] fix(ghidra): prepare ownership before the Java probe deadline #6411

Closed
opened 2026-10-31 01:14:16 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/4795
Author: @J0UH
Created: 10/17/2026
Status: ❌ Closed

Base: main ← Head: fix/ghidra-java-probe-ownership-prep


📝 Commits (2)

  • edc8db3 fix(ghidra): prepare ownership before the Java probe deadline
  • ee88702 fix(ghidra): keep Linux ownership preparation inside the probe deadline

📊 Changes

2 files changed (+212 additions, -0 deletions)

View changed files

📝 src/ghidra/GhidraJavaProbe.ts (+45 -0)
➕ tests/boundary/process/ghidraJavaProbePreparation.test.ts (+167 -0)

📄 Description

Summary

On macOS the first owned command compiles REA's process-ownership helper. The Ghidra Java probe ran that compile inside its 5 s deadline. With a cold Swift module cache, every run timed out before the compile finished, so the cache never warmed. rea doctor reported Command deadline elapsed during ownership preparation., and Ghidra admission rejected the provider with "Update JAVA_HOME to the root of a 64-bit full JDK 21 or newer installation." even though the JDK was fine.

Fixes #4793.

Problem and expected behavior

Smallest trigger: macOS with a cold Swift module cache (after an Xcode or Command Line Tools update, on a fresh machine, or after a cache clear), then rea doctor --provider ghidra --json.

The comment on resolveSwiftcCompileTimeoutMs says the helper compile "stays bounded independently of short provider version probes", through REA_SWIFTC_COMPILE_TIMEOUT_MS (default 120 s). The probe's 5 s deadline cut the compile short instead. With this change, the first run takes as long as the compile needs and later runs are fast.

Change and scope

On macOS, probeGhidraJava now awaits prepareProcessOwnershipInspection(options.signal) before runOwnedCommand starts its deadline. PcapCommand, the Kuna commands and the source-map command already do this (#1139). On Linux, preparation runs a ps check with no time limit of its own, so it stays inside the probe deadline exactly as before.

A small private helper keeps the failure shapes that runOwnedCommand reports for preparation inside its deadline, so the probe's existing error mapping is unchanged. A compiler failure is rethrown as DarwinProcessOwnershipInspectionError and maps to the ownership_preparation stage. Caller cancellation becomes OwnedCommandFailure("cancelled") with any preparation cleanup owner attached.

Not included: runOwnedCommand itself. It includes preparation in its deadline on purpose (tests/boundary/process/ownedCommand.test.ts), and #4143 is editing that file. Other 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. #4793 lists them as a source-only observation, and this PR leaves them alone.

Follow the contribution scope.
Unresolved scope, authority, side effects, or required verification block
integration. Link the required maintainer scope decision for new providers or
capabilities involving target execution, injection, mutation, or protection
recovery; ordinary fixes within existing boundaries may state "not applicable".
Draft PRs may leave the scope decision pending and the readiness and merge checklists
unchecked. Maintainers can help resolve open questions.

Contract and boundary impact

  • Semantic owner and earliest changed stage: the Ghidra Java prerequisite probe (src/ghidra/GhidraJavaProbe.ts), during provider admission.
  • Contribution scope fit and linked maintainer decision: a routine fix within existing boundaries, so not applicable.
  • CLI and MCP/tool-catalog contract: unchanged. Doctor's ghidra-java check and Ghidra admission now pass on a cold cache instead of failing.
  • Provider, bridge, target-format, or platform compatibility: only macOS changes, where the helper compile moves ahead of the deadline. Linux and Windows behave as on the base.
  • Evidence, artifact, provenance, or reconstruction contract: unchanged.
  • Target selection, authority, upstream behavior, and cleanup details (if applicable): no new processes. The existing helper compile and java launch keep their order relative to each other, and the compile now runs before the deadline starts. Preparation still honors caller cancellation and REA_SWIFTC_COMPILE_TIMEOUT_MS, and the probe still retains cleanup owners from a cancelled compile.
  • Generated metadata (docs/public/product-catalog.json), package, or installation impact (CI generates and validates ignored catalogs and skills/; edit authored instructions in .agents/skills/reverse-engineer-anything/ and never commit build outputs): none.

Expected effects (check known effects; use documentation-only on its own).
Unchecked items do not establish absence; leave uncertainties visible.

  • Read existing files or captured data.
  • Launch or attach to a process.
  • Inject instrumentation or run scripts/code.
  • Change process memory, registers, files, or configuration.
  • Contact network services.
  • Recover protected code or work around protections.
  • Documentation or usability only; no target interaction.
  • Not sure yet; investigation or maintainer guidance needed.

The launches are the existing helper compile and java probe; this PR adds none.

Evidence and regression coverage

  • Tests added or updated: tests/boundary/process/ghidraJavaProbePreparation.test.ts, four cases.

    • macOS only: a cold first preparation, with a 2 s probe deadline and a 3 s first preparation before the real prepared inspector. It fails on the base revision with Command deadline elapsed during ownership preparation. and passes with the fix.
    • Linux only: a preparation that stalls (standing in for a stuck ps) is still stopped by the probe deadline. It passes on the base and with the fix, and it hung until the test timed out on this PR's first commit, which is the problem Greptile found.
    • The other two cases pin mapping that already held on the base: cancellation during preparation returns AnalysisCancelledError without launching Java, and a compiler failure reports the ownership_preparation stage with its compiler outcome. No existing test covered either mapping.
  • Base reproduction or other evidence: real Ghidra 12.1.4 with a cold module cache. I created the cold cache by pointing REA_DARWIN_SWIFTC_PATH at a wrapper that runs xcrun swiftc -module-cache-path <empty dir>; the script is in #4793. Runs 1 and 2 share one cache directory.

    Command Base 78cf6100f With this fix
    rea doctor --provider ghidra --json, run 1 ghidra-java fails after 5.5 s, cache stays 173 KiB passes in 15.3 s, cache warms to 89 MiB
    rea doctor --provider ghidra --json, run 2 fails after 5.5 s, cache still 173 KiB passes in 1.3 s
    rea decompile <arm64 fixture> _check_license --provider ghidra, cold cache provider_unavailable after 6 s Ghidra pseudocode after 19 s
  • User-visible CLI/MCP output (if applicable):

    • Base doctor ghidra-java detail: <JAVA_HOME>/bin/java: Command deadline elapsed during ownership preparation.
    • Base decompile rejection: runtime_unavailable, "Update JAVA_HOME to the root of a 64-bit full JDK 21 or newer installation."
    • Fixed doctor detail: 25.0.4.1; 64-bit; JDK; <JAVA_HOME>; accepted a 64-bit full JDK 21 or newer
  • Remaining proof gaps: npm run verify:ghidra could not run. Since e31aa72c9, scripts/verify-real-ghidra.mjs calls the now-async inspectGhidraInstallation without await or a resourceOwner, so the lane stops with Ghidra installation is unavailable: {} before any analysis, on the base and on this branch. I didn't test Intel macOS, Windows, or Linux with a real JDK; the Linux runs use the test file's Java-shaped command.

For evidence-bearing changes (not applicable here; this PR changes no Evidence contract):

  • Observed, derived, and inferred claims remain distinguishable.
  • Artifact identity, source provenance, and failed attempts remain preserved.
  • Unsupported, incomplete, unavailable, or uncertain outcomes remain visible.

Validation performed

  • npm run test:focused -- tests/boundary/process/ghidraJavaProbePreparation.test.ts tests/boundary/filesystem/ghidraJavaProbe.test.ts src/ghidra/GhidraJavaProbe.test.ts src/ghidra/GhidraInstallation.test.ts src/ghidra/GhidraDoctor.test.ts tests/boundary/process/ownedCommand.test.ts tests/boundary/process/ownedCommandCancellation.test.ts tests/boundary/providers/ghidra/ghidraJavaLaunch.test.ts: on macOS, 8 files, 200 tests passed and 1 skipped (the Linux-only case).
  • The new test file on macOS against the base revision, with the source change stashed: the cold case failed, the other two passed.
  • The new test file on Linux in Docker (node:24.18.0-bookworm, arm64), vitest run --project process-boundary: with the fix, 3 passed and 1 skipped (macOS-only). On the base, 3 passed and 1 skipped. On this PR's first commit edc8db3c5, the Linux deadline case hung until the 25 s test timeout.
  • npm run verify:test-discovery: passed.
  • npm run check: typecheck, lint:built, knip, metadata:check and verify:architecture-guards passed. format:check fails on tests/boundary/cli/keyedArchivePrototypeKeyCli.test.ts, which this PR doesn't touch. That file fails the same way on the base. oxfmt --check on the two changed files passes.
  • Pre-push check:fast: passed.
  • npm run verify:ghidra: stops before analysis on the base and on this branch, as described above.

Compatibility, safety, and release

  • Breaking changes or migration steps: none.
  • Real Hopper/Ghidra, browser, or OS coverage: real Ghidra 12.1.4 with Homebrew OpenJDK 25.0.4.1 on macOS 26.6.2 arm64 (Apple M5 Max), Command Line Tools 27.0 (Swift 6.4), Node 24.18.0. Hopper, IDA and browsers aren't involved.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: same processes and ownership checks. On macOS the compile now runs before the probe's deadline starts, so a caller without a signal waits for the full compile, bounded by REA_SWIFTC_COMPILE_TIMEOUT_MS, instead of failing after 5 s. Linux and Windows are unchanged.

Author readiness

Prepared with AI assistance (Claude Code, Claude Opus 5.5). The human owner reviews the diff and evidence and completes these declarations before marking the PR ready.

Human owner: @J0UH

  • I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work.
  • I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps.
  • I declare this PR ready for initial maintainer review.

Before merge

  • This PR addresses a concrete problem or an agreed enhancement.
  • Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/4795 **Author:** [@J0UH](https://github.com/J0UH) **Created:** 10/17/2026 **Status:** ❌ Closed **Base:** `main` ← **Head:** `fix/ghidra-java-probe-ownership-prep` --- ### 📝 Commits (2) - [`edc8db3`](https://github.com/morluto/rea/commit/edc8db3c5d70c01e69db3b52516d5593bd7da9d2) fix(ghidra): prepare ownership before the Java probe deadline - [`ee88702`](https://github.com/morluto/rea/commit/ee88702160cf10aeac2216afbb0d5caead4cd8ee) fix(ghidra): keep Linux ownership preparation inside the probe deadline ### 📊 Changes **2 files changed** (+212 additions, -0 deletions) <details> <summary>View changed files</summary> 📝 `src/ghidra/GhidraJavaProbe.ts` (+45 -0) ➕ `tests/boundary/process/ghidraJavaProbePreparation.test.ts` (+167 -0) </details> ### 📄 Description ## Summary On macOS the first owned command compiles REA's process-ownership helper. The Ghidra Java probe ran that compile inside its 5 s deadline. With a cold Swift module cache, every run timed out before the compile finished, so the cache never warmed. `rea doctor` reported `Command deadline elapsed during ownership preparation.`, and Ghidra admission rejected the provider with "Update JAVA_HOME to the root of a 64-bit full JDK 21 or newer installation." even though the JDK was fine. Fixes #4793. ## Problem and expected behavior Smallest trigger: macOS with a cold Swift module cache (after an Xcode or Command Line Tools update, on a fresh machine, or after a cache clear), then `rea doctor --provider ghidra --json`. The comment on `resolveSwiftcCompileTimeoutMs` says the helper compile "stays bounded independently of short provider version probes", through `REA_SWIFTC_COMPILE_TIMEOUT_MS` (default 120 s). The probe's 5 s deadline cut the compile short instead. With this change, the first run takes as long as the compile needs and later runs are fast. ## Change and scope On macOS, `probeGhidraJava` now awaits `prepareProcessOwnershipInspection(options.signal)` before `runOwnedCommand` starts its deadline. `PcapCommand`, the Kuna commands and the source-map command already do this (#1139). On Linux, preparation runs a `ps` check with no time limit of its own, so it stays inside the probe deadline exactly as before. A small private helper keeps the failure shapes that `runOwnedCommand` reports for preparation inside its deadline, so the probe's existing error mapping is unchanged. A compiler failure is rethrown as `DarwinProcessOwnershipInspectionError` and maps to the `ownership_preparation` stage. Caller cancellation becomes `OwnedCommandFailure("cancelled")` with any preparation cleanup owner attached. Not included: `runOwnedCommand` itself. It includes preparation in its deadline on purpose (`tests/boundary/process/ownedCommand.test.ts`), and #4143 is editing that file. Other 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. #4793 lists them as a source-only observation, and this PR leaves them alone. Follow the [contribution scope](https://github.com/morluto/rea/blob/main/CONTRIBUTING.md#contribution-scope). Unresolved scope, authority, side effects, or required verification block integration. Link the required maintainer scope decision for new providers or capabilities involving target execution, injection, mutation, or protection recovery; ordinary fixes within existing boundaries may state "not applicable". Draft PRs may leave the scope decision pending and the readiness and merge checklists unchecked. Maintainers can help resolve open questions. ## Contract and boundary impact - Semantic owner and earliest changed stage: the Ghidra Java prerequisite probe (`src/ghidra/GhidraJavaProbe.ts`), during provider admission. - Contribution scope fit and linked maintainer decision: a routine fix within existing boundaries, so not applicable. - CLI and MCP/tool-catalog contract: unchanged. Doctor's `ghidra-java` check and Ghidra admission now pass on a cold cache instead of failing. - Provider, bridge, target-format, or platform compatibility: only macOS changes, where the helper compile moves ahead of the deadline. Linux and Windows behave as on the base. - Evidence, artifact, provenance, or reconstruction contract: unchanged. - Target selection, authority, upstream behavior, and cleanup details (if applicable): no new processes. The existing helper compile and `java` launch keep their order relative to each other, and the compile now runs before the deadline starts. Preparation still honors caller cancellation and `REA_SWIFTC_COMPILE_TIMEOUT_MS`, and the probe still retains cleanup owners from a cancelled compile. - Generated metadata (`docs/public/product-catalog.json`), package, or installation impact (CI generates and validates ignored catalogs and `skills/`; edit authored instructions in `.agents/skills/reverse-engineer-anything/` and never commit build outputs): none. Expected effects (check known effects; use documentation-only on its own). Unchecked items do not establish absence; leave uncertainties visible. - [ ] Read existing files or captured data. - [x] Launch or attach to a process. - [ ] Inject instrumentation or run scripts/code. - [ ] Change process memory, registers, files, or configuration. - [ ] Contact network services. - [ ] Recover protected code or work around protections. - [ ] Documentation or usability only; no target interaction. - [ ] Not sure yet; investigation or maintainer guidance needed. The launches are the existing helper compile and `java` probe; this PR adds none. ## Evidence and regression coverage - Tests added or updated: `tests/boundary/process/ghidraJavaProbePreparation.test.ts`, four cases. - macOS only: a cold first preparation, with a 2 s probe deadline and a 3 s first preparation before the real prepared inspector. It fails on the base revision with `Command deadline elapsed during ownership preparation.` and passes with the fix. - Linux only: a preparation that stalls (standing in for a stuck `ps`) is still stopped by the probe deadline. It passes on the base and with the fix, and it hung until the test timed out on this PR's first commit, which is the problem Greptile found. - The other two cases pin mapping that already held on the base: cancellation during preparation returns `AnalysisCancelledError` without launching Java, and a compiler failure reports the `ownership_preparation` stage with its compiler outcome. No existing test covered either mapping. - Base reproduction or other evidence: real Ghidra 12.1.4 with a cold module cache. I created the cold cache by pointing `REA_DARWIN_SWIFTC_PATH` at a wrapper that runs `xcrun swiftc -module-cache-path <empty dir>`; the script is in #4793. Runs 1 and 2 share one cache directory. | Command | Base `78cf6100f` | With this fix | | --- | --- | --- | | `rea doctor --provider ghidra --json`, run 1 | `ghidra-java` fails after 5.5 s, cache stays 173 KiB | passes in 15.3 s, cache warms to 89 MiB | | `rea doctor --provider ghidra --json`, run 2 | fails after 5.5 s, cache still 173 KiB | passes in 1.3 s | | `rea decompile <arm64 fixture> _check_license --provider ghidra`, cold cache | `provider_unavailable` after 6 s | Ghidra pseudocode after 19 s | - User-visible CLI/MCP output (if applicable): - Base doctor `ghidra-java` detail: `<JAVA_HOME>/bin/java: Command deadline elapsed during ownership preparation.` - Base decompile rejection: `runtime_unavailable`, "Update JAVA_HOME to the root of a 64-bit full JDK 21 or newer installation." - Fixed doctor detail: `25.0.4.1; 64-bit; JDK; <JAVA_HOME>; accepted a 64-bit full JDK 21 or newer` - Remaining proof gaps: `npm run verify:ghidra` could not run. Since `e31aa72c9`, `scripts/verify-real-ghidra.mjs` calls the now-async `inspectGhidraInstallation` without `await` or a `resourceOwner`, so the lane stops with `Ghidra installation is unavailable: {}` before any analysis, on the base and on this branch. I didn't test Intel macOS, Windows, or Linux with a real JDK; the Linux runs use the test file's Java-shaped command. For evidence-bearing changes (not applicable here; this PR changes no Evidence contract): - [ ] Observed, derived, and inferred claims remain distinguishable. - [ ] Artifact identity, source provenance, and failed attempts remain preserved. - [ ] Unsupported, incomplete, unavailable, or uncertain outcomes remain visible. ## Validation performed - `npm run test:focused -- tests/boundary/process/ghidraJavaProbePreparation.test.ts tests/boundary/filesystem/ghidraJavaProbe.test.ts src/ghidra/GhidraJavaProbe.test.ts src/ghidra/GhidraInstallation.test.ts src/ghidra/GhidraDoctor.test.ts tests/boundary/process/ownedCommand.test.ts tests/boundary/process/ownedCommandCancellation.test.ts tests/boundary/providers/ghidra/ghidraJavaLaunch.test.ts`: on macOS, 8 files, 200 tests passed and 1 skipped (the Linux-only case). - The new test file on macOS against the base revision, with the source change stashed: the cold case failed, the other two passed. - The new test file on Linux in Docker (`node:24.18.0-bookworm`, arm64), `vitest run --project process-boundary`: with the fix, 3 passed and 1 skipped (macOS-only). On the base, 3 passed and 1 skipped. On this PR's first commit `edc8db3c5`, the Linux deadline case hung until the 25 s test timeout. - `npm run verify:test-discovery`: passed. - `npm run check`: `typecheck`, `lint:built`, `knip`, `metadata:check` and `verify:architecture-guards` passed. `format:check` fails on `tests/boundary/cli/keyedArchivePrototypeKeyCli.test.ts`, which this PR doesn't touch. That file fails the same way on the base. `oxfmt --check` on the two changed files passes. - Pre-push `check:fast`: passed. - `npm run verify:ghidra`: stops before analysis on the base and on this branch, as described above. ## Compatibility, safety, and release - Breaking changes or migration steps: none. - Real Hopper/Ghidra, browser, or OS coverage: real Ghidra 12.1.4 with Homebrew OpenJDK 25.0.4.1 on macOS 26.6.2 arm64 (Apple M5 Max), Command Line Tools 27.0 (Swift 6.4), Node 24.18.0. Hopper, IDA and browsers aren't involved. - Package or release metadata impact: none. - Security, privacy, process, or containment review: same processes and ownership checks. On macOS the compile now runs before the probe's deadline starts, so a caller without a signal waits for the full compile, bounded by `REA_SWIFTC_COMPILE_TIMEOUT_MS`, instead of failing after 5 s. Linux and Windows are unchanged. ## Author readiness <!-- The human owner completes these declarations before requesting review. The PR author is the owner by default. A different owner must explicitly accept ownership on this PR. AI agents must leave these declarations to a human to dictate to them or there could be catastrophic consequences for many users of REA. Draft PRs may leave them unchecked. These declarations record initial readiness when opening the PR for review; later pushes do not require a new human sign-off. Keep the description and validation evidence current as the work changes. See CONTRIBUTING.md#pull-request-ownership-and-readiness. --> Prepared with AI assistance (Claude Code, Claude Opus 5.5). The human owner reviews the diff and evidence and completes these declarations before marking the PR ready. Human owner: @J0UH - [ ] I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work. - [ ] I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps. - [ ] I declare this PR ready for initial maintainer review. ## Before merge <!-- Draft PRs may leave pending items unchecked. Resolve material scope and authority questions before integration. --> - [ ] This PR addresses a concrete problem or an agreed enhancement. - [ ] Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff. - [ ] The PR has one focused outcome and the title follows `type(scope): outcome`. - [ ] Related issue is linked, or the reason for not linking one is stated above. - [ ] Tests cover changed observable behavior and meaningful failure paths. - [ ] Owning docs, contracts, and generated metadata are updated where needed. - [ ] User-visible CLI/MCP changes include representative output. - [ ] I checked the final diff for secrets, unrelated cleanup, and unsupported claims. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-31 01:14:16 +00:00
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#6411
No description provided.