[PR #2792] [MERGED] fix(ghidra): fail doctor when the JVM os.arch selects an unvalidated decompiler directory #5396

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

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/2792
Author: @sx4im
Created: 10/13/2026
Status: ✅ Merged
Merged: 10/13/2026
Merged by: @morluto

Base: main ← Head: fix/2785-ghidra-jvm-osarch-decompiler


📝 Commits (10+)

  • b23460b fix(ghidra): fail doctor when the JVM os.arch selects an unvalidated decompiler directory
  • 53bb0c1 fix(ghidra): tighten JVM arch remediation and cover os.arch extraction
  • 295d81c fix(ghidra): refuse unrecognized JVM native architectures
  • d005343 docs(ghidra): align doctor prerequisites with JVM architecture admission
  • c523d1a fix(ghidra): reject unreported JVM architecture
  • f2b98fd test(ghidra): separate JVM and executable admission cases
  • 5f84c61 fix(ghidra): defer decompiler arch validation when Java is absent
  • 5730da1 fix(ghidra): reject JVM native architecture override
  • 62e4229 fix(ghidra): validate JVM platform overrides
  • 1dbfac8 fix(ghidra): validate JVM os.name and helper-environment platform overrides

📊 Changes

14 files changed (+593 additions, -54 deletions)

View changed files

📝 docs/installation.md (+12 -1)
📝 src/ghidra/GhidraDoctor.test.ts (+6 -1)
📝 src/ghidra/GhidraInstallation.fixture.ts (+2 -0)
📝 src/ghidra/GhidraInstallation.test.ts (+238 -3)
📝 src/ghidra/GhidraInstallation.ts (+126 -0)
📝 src/ghidra/GhidraJavaLaunch.ts (+62 -2)
📝 src/ghidra/GhidraLauncher.ts (+41 -36)
📝 src/ghidra/GhidraProvider.behavior.test.ts (+17 -9)
📝 src/ghidra/GhidraSnapshotCache.test.ts (+2 -0)
📝 tests/boundary/filesystem/ghidraPspExtension.test.ts (+2 -0)
📝 tests/boundary/mcp/ghidraMcpHarness.ts (+2 -0)
📝 tests/boundary/mcp/ghidraReopen.test.ts (+2 -0)
📝 tests/boundary/providers/ghidra/ghidraJavaLaunch.test.ts (+67 -2)
📝 tests/boundary/providers/ghidra/ghidraLauncher.test.ts (+14 -0)

📄 Description

Summary

rea doctor --provider ghidra reported a healthy Ghidra installation while every analysis failed with execution_failure, when the JDK Ghidra runs on has a different architecture than the host (x86_64 Temurin under Rosetta on Apple silicon, decompiler built for mac_arm_64). Doctor validated the native decompiler for Node's process.arch; Ghidra picks the decompiler directory from the JVM's os.arch, which probeJava never read. Now probeJava reads os.arch, and the native_decompiler check fails with a remediation pointing at a matching-arch JDK when the JVM would select a different, unvalidated directory.

Fixes #2785

Problem and expected behavior

Smallest trigger from the issue: Apple silicon host, Ghidra 12.1.4 with only Ghidra/Features/Decompiler/build/os/mac_arm_64/decompile built, and an x86_64 JDK 21 as the default java. rea doctor --provider ghidra --json returned healthy: true; rea function <arm64 Mach-O> <function> returned execution_failure every time. The diagnostics showed ERROR os/mac_x86_64/decompile does not exist from Ghidra's DecompileProcessFactory. Expected: doctor reports the provider as unavailable in this state, with a remediation such as pointing JAVA_HOME at an arm64 JDK.

Change and scope

  • probeJava in src/ghidra/GhidraInstallation.ts now also reads the os.arch property from the existing java -XshowSettings:properties -version output into GhidraJavaObservation.osArch (omitted when the probe cannot establish it).
  • nativeDecompilerCheck fails the check when the observed os.arch maps to a different native directory than the one validated from the host architecture, with detail naming both directories and a remediation to point JAVA_HOME at a JDK matching the built decompiler. Unrecognized os.arch values keep the previous behavior (unknown stays unknown).
  • The observed value is exposed as java_os_arch in ghidraInstallationDiagnostics.

Contribution scope fit: ordinary fix within existing boundaries; no new providers or capabilities, no change to target execution, injection, mutation, or protection recovery. Maintainer scope decision: not applicable.

Contract and boundary impact

  • Semantic owner and earliest changed stage: Ghidra installation inspection (inspectGhidraInstallation), consumed by rea doctor and provider registration via projectGhidraDoctorInspection.
  • Contribution scope fit and linked maintainer decision: ordinary fix; not applicable.
  • CLI and MCP/tool-catalog contract: doctor JSON gains a java_os_arch diagnostic key (null when unobserved); the native_decompiler check can now report failed where it previously passed, with remediation text. No tool schema changes.
  • Provider, bridge, target-format, or platform compatibility: none.
  • Evidence, artifact, provenance, or reconstruction contract: none.
  • Target selection, authority, upstream behavior, and cleanup details: not applicable.
  • Generated metadata, package, or installation impact: none.

Expected effects:

  • 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 two checked items are the pre-existing java -XshowSettings -version probe and install-path reads; this change only parses one more property from the same output.)

Evidence and regression coverage

  • Tests added or updated: three cases in src/ghidra/GhidraInstallation.test.ts under "Ghidra native decompiler admission": x86_64 JDK on darwin/arm64 now yields unavailable with native_decompiler failed; matching aarch64 JDK stays available; unrecognized riscv64 os.arch stays available.
  • Base reproduction or other evidence: the new mismatch test fails on the base revision (inspection comes back available, exactly the reported false-healthy) and passes with the fix.
  • User-visible CLI/MCP output: the failed check renders as ghidra-native_decompiler not ok with the JAVA_HOME remediation in doctor output.
  • Remaining proof gaps: no Apple-silicon host or Rosetta JDK in this environment, so the end-to-end Rosetta scenario itself was not executed; verification is at the unit level against the real inspection code path that doctor uses.

For evidence-bearing changes:

  • 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

  • GhidraInstallation.test.ts -- 54/54 pass. vitest workers SIGBUS in this sandbox, so the run used an esbuild bundle of the real test file executed under node with a minimal describe/it/expect shim against the real production modules (the shim was cross-checked: the full suite passes identically, and the new mismatch test fails on the unfixed code).
  • oxlint on both changed files -- 0 errors, 0 warnings.
  • tsc --noEmit could not run here: the repo pins TypeScript 7 whose native binary segfaults in this sandbox. The changes reuse the file's existing typed patterns (optional observation fields, the NATIVE_PLATFORMS lookup, the installationCheck constructor).

Compatibility, safety, and release

  • Breaking changes or migration steps: none. Behavior change is the intended fix: doctor now reports the Ghidra provider unavailable (previously healthy) when the JVM architecture mismatches the validated decompiler.
  • Real Hopper/Ghidra, browser, or OS coverage: unit-level only; no real Ghidra or Rosetta JDK available in this environment.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: the change only reads one more JVM property from the existing probe output; no new process launches, no network, no file writes.

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/2792 **Author:** [@sx4im](https://github.com/sx4im) **Created:** 10/13/2026 **Status:** ✅ Merged **Merged:** 10/13/2026 **Merged by:** [@morluto](https://github.com/morluto) **Base:** `main` ← **Head:** `fix/2785-ghidra-jvm-osarch-decompiler` --- ### 📝 Commits (10+) - [`b23460b`](https://github.com/morluto/rea/commit/b23460b4bcb4ebff654ec01a981cca32ff5b378b) fix(ghidra): fail doctor when the JVM os.arch selects an unvalidated decompiler directory - [`53bb0c1`](https://github.com/morluto/rea/commit/53bb0c1c2d70450af27e8f85ea0de015d72c9ca5) fix(ghidra): tighten JVM arch remediation and cover os.arch extraction - [`295d81c`](https://github.com/morluto/rea/commit/295d81ce36c97b60a3772a265b57a087f06d4f3b) fix(ghidra): refuse unrecognized JVM native architectures - [`d005343`](https://github.com/morluto/rea/commit/d0053431977bb36dc0634aec63d05fbc4758eb0b) docs(ghidra): align doctor prerequisites with JVM architecture admission - [`c523d1a`](https://github.com/morluto/rea/commit/c523d1acd0992fa5ef510756926b06995776dc59) fix(ghidra): reject unreported JVM architecture - [`f2b98fd`](https://github.com/morluto/rea/commit/f2b98fd97d9d0f135096f96370e1b4ddeda063ba) test(ghidra): separate JVM and executable admission cases - [`5f84c61`](https://github.com/morluto/rea/commit/5f84c613059bbe5ed46a1ebc258d032d339c9ceb) fix(ghidra): defer decompiler arch validation when Java is absent - [`5730da1`](https://github.com/morluto/rea/commit/5730da13fee5fe90c9b07b1b379378821002650e) fix(ghidra): reject JVM native architecture override - [`62e4229`](https://github.com/morluto/rea/commit/62e422953d7ab7dabfd9b2a93dbf3b21f9966be1) fix(ghidra): validate JVM platform overrides - [`1dbfac8`](https://github.com/morluto/rea/commit/1dbfac8b2540b013c24d882c40afc05a65f687c1) fix(ghidra): validate JVM os.name and helper-environment platform overrides ### 📊 Changes **14 files changed** (+593 additions, -54 deletions) <details> <summary>View changed files</summary> 📝 `docs/installation.md` (+12 -1) 📝 `src/ghidra/GhidraDoctor.test.ts` (+6 -1) 📝 `src/ghidra/GhidraInstallation.fixture.ts` (+2 -0) 📝 `src/ghidra/GhidraInstallation.test.ts` (+238 -3) 📝 `src/ghidra/GhidraInstallation.ts` (+126 -0) 📝 `src/ghidra/GhidraJavaLaunch.ts` (+62 -2) 📝 `src/ghidra/GhidraLauncher.ts` (+41 -36) 📝 `src/ghidra/GhidraProvider.behavior.test.ts` (+17 -9) 📝 `src/ghidra/GhidraSnapshotCache.test.ts` (+2 -0) 📝 `tests/boundary/filesystem/ghidraPspExtension.test.ts` (+2 -0) 📝 `tests/boundary/mcp/ghidraMcpHarness.ts` (+2 -0) 📝 `tests/boundary/mcp/ghidraReopen.test.ts` (+2 -0) 📝 `tests/boundary/providers/ghidra/ghidraJavaLaunch.test.ts` (+67 -2) 📝 `tests/boundary/providers/ghidra/ghidraLauncher.test.ts` (+14 -0) </details> ### 📄 Description ## Summary `rea doctor --provider ghidra` reported a healthy Ghidra installation while every analysis failed with `execution_failure`, when the JDK Ghidra runs on has a different architecture than the host (x86_64 Temurin under Rosetta on Apple silicon, decompiler built for `mac_arm_64`). Doctor validated the native decompiler for Node's `process.arch`; Ghidra picks the decompiler directory from the JVM's `os.arch`, which `probeJava` never read. Now `probeJava` reads `os.arch`, and the `native_decompiler` check fails with a remediation pointing at a matching-arch JDK when the JVM would select a different, unvalidated directory. Fixes #2785 ## Problem and expected behavior Smallest trigger from the issue: Apple silicon host, Ghidra 12.1.4 with only `Ghidra/Features/Decompiler/build/os/mac_arm_64/decompile` built, and an x86_64 JDK 21 as the default `java`. `rea doctor --provider ghidra --json` returned `healthy: true`; `rea function <arm64 Mach-O> <function>` returned `execution_failure` every time. The diagnostics showed `ERROR os/mac_x86_64/decompile does not exist` from Ghidra's `DecompileProcessFactory`. Expected: doctor reports the provider as unavailable in this state, with a remediation such as pointing `JAVA_HOME` at an arm64 JDK. ## Change and scope - `probeJava` in `src/ghidra/GhidraInstallation.ts` now also reads the `os.arch` property from the existing `java -XshowSettings:properties -version` output into `GhidraJavaObservation.osArch` (omitted when the probe cannot establish it). - `nativeDecompilerCheck` fails the check when the observed `os.arch` maps to a different native directory than the one validated from the host architecture, with detail naming both directories and a remediation to point `JAVA_HOME` at a JDK matching the built decompiler. Unrecognized `os.arch` values keep the previous behavior (unknown stays unknown). - The observed value is exposed as `java_os_arch` in `ghidraInstallationDiagnostics`. Contribution scope fit: ordinary fix within existing boundaries; no new providers or capabilities, no change to target execution, injection, mutation, or protection recovery. Maintainer scope decision: not applicable. ## Contract and boundary impact - Semantic owner and earliest changed stage: Ghidra installation inspection (`inspectGhidraInstallation`), consumed by `rea doctor` and provider registration via `projectGhidraDoctorInspection`. - Contribution scope fit and linked maintainer decision: ordinary fix; not applicable. - CLI and MCP/tool-catalog contract: doctor JSON gains a `java_os_arch` diagnostic key (null when unobserved); the `native_decompiler` check can now report failed where it previously passed, with remediation text. No tool schema changes. - Provider, bridge, target-format, or platform compatibility: none. - Evidence, artifact, provenance, or reconstruction contract: none. - Target selection, authority, upstream behavior, and cleanup details: not applicable. - Generated metadata, package, or installation impact: none. Expected effects: - [x] 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 two checked items are the pre-existing `java -XshowSettings -version` probe and install-path reads; this change only parses one more property from the same output.) ## Evidence and regression coverage - Tests added or updated: three cases in `src/ghidra/GhidraInstallation.test.ts` under "Ghidra native decompiler admission": x86_64 JDK on darwin/arm64 now yields `unavailable` with `native_decompiler` failed; matching `aarch64` JDK stays `available`; unrecognized `riscv64` `os.arch` stays `available`. - Base reproduction or other evidence: the new mismatch test fails on the base revision (inspection comes back `available`, exactly the reported false-healthy) and passes with the fix. - User-visible CLI/MCP output: the failed check renders as `ghidra-native_decompiler` not ok with the `JAVA_HOME` remediation in doctor output. - Remaining proof gaps: no Apple-silicon host or Rosetta JDK in this environment, so the end-to-end Rosetta scenario itself was not executed; verification is at the unit level against the real inspection code path that doctor uses. For evidence-bearing changes: - [x] Observed, derived, and inferred claims remain distinguishable. - [x] Artifact identity, source provenance, and failed attempts remain preserved. - [x] Unsupported, incomplete, unavailable, or uncertain outcomes remain visible. ## Validation performed - `GhidraInstallation.test.ts` -- 54/54 pass. vitest workers SIGBUS in this sandbox, so the run used an esbuild bundle of the real test file executed under node with a minimal describe/it/expect shim against the real production modules (the shim was cross-checked: the full suite passes identically, and the new mismatch test fails on the unfixed code). - `oxlint` on both changed files -- 0 errors, 0 warnings. - `tsc --noEmit` could not run here: the repo pins TypeScript 7 whose native binary segfaults in this sandbox. The changes reuse the file's existing typed patterns (optional observation fields, the `NATIVE_PLATFORMS` lookup, the `installationCheck` constructor). ## Compatibility, safety, and release - Breaking changes or migration steps: none. Behavior change is the intended fix: doctor now reports the Ghidra provider unavailable (previously healthy) when the JVM architecture mismatches the validated decompiler. - Real Hopper/Ghidra, browser, or OS coverage: unit-level only; no real Ghidra or Rosetta JDK available in this environment. - Package or release metadata impact: none. - Security, privacy, process, or containment review: the change only reads one more JVM property from the existing probe output; no new process launches, no network, no file writes. ## Before merge - [x] This PR addresses a concrete problem or an agreed enhancement. - [x] Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff. - [x] The PR has one focused outcome and the title follows `type(scope): outcome`. - [x] Related issue is linked, or the reason for not linking one is stated above. - [x] Tests cover changed observable behavior and meaningful failure paths. - [x] Owning docs, contracts, and generated metadata are updated where needed. - [x] User-visible CLI/MCP changes include representative output. - [x] 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:06:19 +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#5396
No description provided.