[GH-ISSUE #8171] [Bug] Ghidra verifier drops the discovered JDK when JAVA_HOME is unset #4697

Open
opened 2026-10-31 00:52:35 +00:00 by bsakaguchi · 7 comments
Owner

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

Before submitting

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

Area

Native analysis or decompilation

Observed behavior

npm run verify:ghidra fails at startup with POSIX Ghidra requires its inspected JDK home when GHIDRA_INSTALL_DIR is set, a supported JDK is discoverable on PATH, and JAVA_HOME is unset. On the same host, rea doctor --provider ghidra reports Ghidra 12.1.4 and a healthy 64-bit JDK 21; normal REA CLI and MCP Ghidra analysis worked. Setting JAVA_HOME allowed the verifier to pass this startup stage, though it later failed independently at the MCP comparison input limit.

Reproduction

On macOS arm64 with Node 24.22.1, Ghidra 12.1.4 and a discoverable JDK 21, unset JAVA_HOME, set GHIDRA_INSTALL_DIR, then run npm run verify:ghidra. Observed once. The retry with a valid explicit JAVA_HOME got beyond the launcher; it did not pass the full verification lane.

Expected behavior

Use the JDK selected and validated by the existing installation inspection for the verifier launcher, matching the normal provider path, or explicitly report the verifier's additional prerequisite before starting. Preserve explicit caller selections and identity verification.

Impact and scope

The real-provider verification gate fails before testing Ghidra on a host where the ordinary REA provider works. No claim of a production provider failure. Other verifier lanes and hosts are unverified.

REA version

Source checkout 47fa6ab3795d547207001ffb01643c0aa39dcad8.

Runtime and platform

macOS arm64, Node 24.22.1, Ghidra 12.1.4, JDK 21.0.12.1.

Interface

Other

Earliest failure stage

Setup or provider admission

Relevant output or evidence

GhidraLaunchError: POSIX Ghidra requires its inspected JDK home. scripts/verify-real-ghidra.mjs obtains installation via inspectVerifierGhidraInstallation but constructs the launcher with process.env.JAVA_HOME rather than the inspected installation.javaHome; src/ghidra/GhidraProviderClient.ts passes the installation's JDK. The later failure after setting JAVA_HOME needs separate diagnosis.

Originally created by @morluto on GitHub (Oct 30, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/8171 ### 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 Native analysis or decompilation ### Observed behavior `npm run verify:ghidra` fails at startup with `POSIX Ghidra requires its inspected JDK home` when `GHIDRA_INSTALL_DIR` is set, a supported JDK is discoverable on PATH, and `JAVA_HOME` is unset. On the same host, `rea doctor --provider ghidra` reports Ghidra 12.1.4 and a healthy 64-bit JDK 21; normal REA CLI and MCP Ghidra analysis worked. Setting `JAVA_HOME` allowed the verifier to pass this startup stage, though it later failed independently at the MCP comparison input limit. ### Reproduction On macOS arm64 with Node 24.22.1, Ghidra 12.1.4 and a discoverable JDK 21, unset `JAVA_HOME`, set `GHIDRA_INSTALL_DIR`, then run `npm run verify:ghidra`. Observed once. The retry with a valid explicit `JAVA_HOME` got beyond the launcher; it did not pass the full verification lane. ### Expected behavior Use the JDK selected and validated by the existing installation inspection for the verifier launcher, matching the normal provider path, or explicitly report the verifier's additional prerequisite before starting. Preserve explicit caller selections and identity verification. ### Impact and scope The real-provider verification gate fails before testing Ghidra on a host where the ordinary REA provider works. No claim of a production provider failure. Other verifier lanes and hosts are unverified. ### REA version Source checkout `47fa6ab3795d547207001ffb01643c0aa39dcad8`. ### Runtime and platform macOS arm64, Node 24.22.1, Ghidra 12.1.4, JDK 21.0.12.1. ### Interface Other ### Earliest failure stage Setup or provider admission ### Relevant output or evidence `GhidraLaunchError: POSIX Ghidra requires its inspected JDK home`. `scripts/verify-real-ghidra.mjs` obtains `installation` via `inspectVerifierGhidraInstallation` but constructs the launcher with `process.env.JAVA_HOME` rather than the inspected `installation.javaHome`; `src/ghidra/GhidraProviderClient.ts` passes the installation's JDK. The later failure after setting JAVA_HOME needs separate diagnosis.
Author
Owner

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

Looping in @gensokyo-dev @kaoru0822-kitauji @N0zoM1z0 for visibility on the Ghidra, Kuna, and REA tool-design investigation.

<!-- gh-comment-id:6538783739 --> @morluto commented on GitHub (Oct 31, 2026): Looping in @gensokyo-dev @kaoru0822-kitauji @N0zoM1z0 for visibility on the Ghidra, Kuna, and REA tool-design investigation.
Author
Owner

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

Retested on main e6d061897 with macOS arm64, Node 24.22.1, Ghidra 12.1.4 and JDK 21.0.12.1. In all three commands below JAVA_HOME was absent and the full JDK's bin was explicitly on PATH:

  1. rea doctor --provider ghidra --json exited 0 and reported healthy, with the discovered full JDK 21 home.
  2. npm run verify:ghidra built its runtime and exited 1 before the Ghidra lane could run, with POSIX Ghidra requires its inspected JDK home.
  3. Real rea decompile <self-built-Mach-O> _evaluate --provider ghidra --format json exited 0 and returned Ghidra pseudocode.

This reconfirms the verifier/provider difference on one host; it does not imply that every JDK arrangement or verifier lane has the same behavior. #8258 addresses the separate Evidence-input limit and its full real-Ghidra control used explicit JAVA_HOME, so its passing result should not be taken as verification of this issue.

<!-- gh-comment-id:6540803632 --> @morluto commented on GitHub (Oct 31, 2026): Retested on main `e6d061897` with macOS arm64, Node 24.22.1, Ghidra 12.1.4 and JDK 21.0.12.1. In all three commands below `JAVA_HOME` was absent and the full JDK's `bin` was explicitly on `PATH`: 1. `rea doctor --provider ghidra --json` exited 0 and reported healthy, with the discovered full JDK 21 home. 2. `npm run verify:ghidra` built its runtime and exited 1 before the Ghidra lane could run, with `POSIX Ghidra requires its inspected JDK home`. 3. Real `rea decompile <self-built-Mach-O> _evaluate --provider ghidra --format json` exited 0 and returned Ghidra pseudocode. This reconfirms the verifier/provider difference on one host; it does not imply that every JDK arrangement or verifier lane has the same behavior. [#8258](https://github.com/morluto/rea/pull/8258) addresses the separate Evidence-input limit and its full real-Ghidra control used explicit `JAVA_HOME`, so its passing result should not be taken as verification of this issue.
Author
Owner

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

Reproduced on current REA a989c0e458c91af177e097abd7be0fd021b0401d with Ghidra 12.1.4, Node 24.22.1 and JDK 21.0.12.1 on macOS arm64: with the JDK's bin first on PATH and JAVA_HOME unset, rea doctor --provider ghidra --target <existing fixture> --format json returned healthy and identified the JDK, but npm run verify:ghidra failed with POSIX Ghidra requires its inspected JDK home. With JAVA_HOME explicitly set, the verifier passed this stage and encountered the separately reported #8172 input-limit failure. Neither run completed the Ghidra lane.

<!-- gh-comment-id:6545698284 --> @morluto commented on GitHub (Oct 31, 2026): Reproduced on current REA `a989c0e458c91af177e097abd7be0fd021b0401d` with Ghidra 12.1.4, Node 24.22.1 and JDK 21.0.12.1 on macOS arm64: with the JDK's `bin` first on `PATH` and `JAVA_HOME` unset, `rea doctor --provider ghidra --target <existing fixture> --format json` returned healthy and identified the JDK, but `npm run verify:ghidra` failed with `POSIX Ghidra requires its inspected JDK home`. With `JAVA_HOME` explicitly set, the verifier passed this stage and encountered the separately reported #8172 input-limit failure. Neither run completed the Ghidra lane.
Author
Owner

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

A paired experiment on current main 60149282 confirms the producing boundary. In a detached worktree I applied open PR #8258's comparison fix (the two PR files exactly match head 2bf92eb91964d136256116badbf50001dfabedf0) and changed the verifier's direct GhidraHeadlessLauncher construction to pass installation.javaHome, which the existing prerequisite inspection has already validated. The one-line verifier change was not submitted.

With JAVA_HOME unset, JDK 21.0.12.1 on PATH, Ghidra 12.1.4 and Node 24.22.1 on macOS arm64, the inspection reported that JDK as available. The full npm run verify:ghidra host-native lane then completed with ok: true, four fixture variants, native-value trace 171 nodes / 298 edges, cleanup: complete, and a verified verifier lineage with no descendants at report time. Earlier on this same main, the unmodified verifier failed at startup without JAVA_HOME; with explicit JAVA_HOME it reached the separate comparison-limit failure. PR #8258 is included here to get past that independent later failure, so this is not evidence that the JDK change alone fixes #8172.

As separate inspection-only controls, a valid explicitly selected JDK was retained, while an invalid explicit JDK was reported unavailable rather than silently replaced by the PATH JDK. This does not establish full verifier behavior for every JDK layout, OS, or failure/cleanup stage. It suggests testing the inspected-value handoff through a real completed lane while preserving explicit selection, rather than teaching the verifier a second JDK discovery algorithm.

<!-- gh-comment-id:6546528611 --> @morluto commented on GitHub (Oct 31, 2026): A paired experiment on current main `60149282` confirms the producing boundary. In a detached worktree I applied open PR #8258's comparison fix (the two PR files exactly match head `2bf92eb91964d136256116badbf50001dfabedf0`) and changed the verifier's direct `GhidraHeadlessLauncher` construction to pass `installation.javaHome`, which the existing prerequisite inspection has already validated. The one-line verifier change was not submitted. With `JAVA_HOME` unset, JDK 21.0.12.1 on PATH, Ghidra 12.1.4 and Node 24.22.1 on macOS arm64, the inspection reported that JDK as available. The full `npm run verify:ghidra` host-native lane then completed with `ok: true`, four fixture variants, native-value trace 171 nodes / 298 edges, `cleanup: complete`, and a verified verifier lineage with no descendants at report time. Earlier on this same main, the unmodified verifier failed at startup without `JAVA_HOME`; with explicit `JAVA_HOME` it reached the separate comparison-limit failure. PR #8258 is included here to get past that independent later failure, so this is not evidence that the JDK change alone fixes #8172. As separate **inspection-only** controls, a valid explicitly selected JDK was retained, while an invalid explicit JDK was reported unavailable rather than silently replaced by the PATH JDK. This does not establish full verifier behavior for every JDK layout, OS, or failure/cleanup stage. It suggests testing the inspected-value handoff through a real completed lane while preserving explicit selection, rather than teaching the verifier a second JDK discovery algorithm.
Author
Owner

@morluto commented on GitHub (Nov 1, 2026):

Additional real-verifier controls on REA main 6e9ee97a69cb16c7150d8b550e705cba055ee4f0, macOS arm64, Node 24.22.1, Ghidra 12.1.4 and JDK 21:

  • With JAVA_HOME unset and the JDK's bin on PATH, node scripts/verify-real-ghidra-com.mjs completed successfully with mocked: false, CLI and stdio MCP checks, original_unchanged: true, cleanup: complete, and a verified empty descendant list. It analyzed its generated COM fixture, which was not executed.
  • With the same valid JDK on PATH but JAVA_HOME explicitly set to an owned nonexistent path, node scripts/verify-real-ghidra.mjs exited 1 at prerequisite inspection with runtime_missing, naming the explicit path and advising an update to JAVA_HOME; it did not silently fall back to PATH.

These complement the previously reported general-lane no-JAVA_HOME failure and combined-tree full-lane experiment. Source inspection shows the COM verifier passes PATH into its actual MCP child, which performs normal provider discovery, while the general verifier directly constructs GhidraHeadlessLauncher without forwarding the inspected installation.javaHome when JAVA_HOME is unset. Thus this control narrows the failure to that verifier path; it does not establish that other lanes or JDK arrangements work.

A useful regression for the eventual fix would execute the actual general verifier on a provider-equipped host with JAVA_HOME unset and a verified JDK on PATH, assert completion and cleanup after the independent #8172 fix, and separately assert explicit-invalid selection fails rather than falling back. Merely testing the installation inspector or only the COM lane would not exercise this launcher boundary. The present main-revision general lane cannot be claimed to pass because #8172 remains open.

<!-- gh-comment-id:6553122329 --> @morluto commented on GitHub (Nov 1, 2026): Additional real-verifier controls on REA main `6e9ee97a69cb16c7150d8b550e705cba055ee4f0`, macOS arm64, Node 24.22.1, Ghidra 12.1.4 and JDK 21: - With `JAVA_HOME` unset and the JDK's `bin` on `PATH`, `node scripts/verify-real-ghidra-com.mjs` completed successfully with `mocked: false`, CLI and stdio MCP checks, `original_unchanged: true`, `cleanup: complete`, and a verified empty descendant list. It analyzed its generated COM fixture, which was not executed. - With the same valid JDK on `PATH` but `JAVA_HOME` explicitly set to an owned nonexistent path, `node scripts/verify-real-ghidra.mjs` exited 1 at prerequisite inspection with `runtime_missing`, naming the explicit path and advising an update to `JAVA_HOME`; it did not silently fall back to `PATH`. These complement the previously reported general-lane no-`JAVA_HOME` failure and combined-tree full-lane experiment. Source inspection shows the COM verifier passes `PATH` into its actual MCP child, which performs normal provider discovery, while the general verifier directly constructs `GhidraHeadlessLauncher` without forwarding the inspected `installation.javaHome` when `JAVA_HOME` is unset. Thus this control narrows the failure to that verifier path; it does not establish that other lanes or JDK arrangements work. A useful regression for the eventual fix would execute the actual general verifier on a provider-equipped host with `JAVA_HOME` unset and a verified JDK on `PATH`, assert completion and cleanup after the independent #8172 fix, and separately assert explicit-invalid selection fails rather than falling back. Merely testing the installation inspector or only the COM lane would not exercise this launcher boundary. The present main-revision general lane cannot be claimed to pass because #8172 remains open.
Author
Owner

@morluto commented on GitHub (Nov 1, 2026):

Draft #8440 passes the inspected JDK home to the existing verifier launcher. On main 6e9ee97a, a no-JAVA_HOME / valid PATH JDK control failed with the original launcher error; on the one-line candidate it reached the independent #8172 input limit. Explicit valid selection reached the same later check, while an invalid explicit JAVA_HOME was rejected during inspection. A full no-JAVA_HOME real Ghidra lane passed only in a combined test tree also containing #8258 and the snapshot-verifier fix #8381; the PR records that scope and does not claim a standalone full pass.

<!-- gh-comment-id:6553339106 --> @morluto commented on GitHub (Nov 1, 2026): Draft #8440 passes the inspected JDK home to the existing verifier launcher. On main `6e9ee97a`, a no-`JAVA_HOME` / valid PATH JDK control failed with the original launcher error; on the one-line candidate it reached the independent #8172 input limit. Explicit valid selection reached the same later check, while an invalid explicit `JAVA_HOME` was rejected during inspection. A full no-`JAVA_HOME` real Ghidra lane passed only in a combined test tree also containing #8258 and the snapshot-verifier fix #8381; the PR records that scope and does not claim a standalone full pass.
Author
Owner

@morluto commented on GitHub (Nov 1, 2026):

Source audit on 2026-11-01 confirms this handoff mismatch remains at 3842809c371a59609f576a9db8f9c492a3f071e6 (relevant files unchanged at 53363166f068a21f00a605187e1c3af428aa7d5d).

  • scripts/verify-real-ghidra.mjs:52 obtains installation through inspectVerifierGhidraInstallation.
  • At lines 491–497, it constructs GhidraHeadlessLauncher from process.env.JAVA_HOME, omitting Java home when that environment variable is unset.
  • The production provider instead passes prerequisites.value.javaHome in GhidraProviderClient.ts:458, derived from installation.javaHome.

The structural fix is to consume the installation owner's selected JDK in the verifier, rather than infer selection again from the original environment. Keep explicit caller choices and the validated architecture/version facts. A regression should cover an unset JAVA_HOME with a successfully discovered JDK and then use that JDK for launch.

I did not rerun the full real-provider verifier in this audit. Separately, actual scoped doctor calls on macOS arm64 showed an installed openjdk@21 was missed automatically but worked with explicit JAVA_HOME; that discovery limitation is now #8504 and is distinct from this verifier handoff.

Upstream research: Gradle's toolchain inspection and launcher guidance provides prior art for retaining detected runtime metadata through selection and execution. This is a design reference, not verification of REA or a proposal to auto-install JDKs.

<!-- gh-comment-id:6558187757 --> @morluto commented on GitHub (Nov 1, 2026): Source audit on 2026-11-01 confirms this handoff mismatch remains at `3842809c371a59609f576a9db8f9c492a3f071e6` (relevant files unchanged at `53363166f068a21f00a605187e1c3af428aa7d5d`). - `scripts/verify-real-ghidra.mjs:52` obtains `installation` through `inspectVerifierGhidraInstallation`. - At lines 491–497, it constructs `GhidraHeadlessLauncher` from `process.env.JAVA_HOME`, omitting Java home when that environment variable is unset. - The production provider instead passes `prerequisites.value.javaHome` in `GhidraProviderClient.ts:458`, derived from `installation.javaHome`. The structural fix is to consume the installation owner's selected JDK in the verifier, rather than infer selection again from the original environment. Keep explicit caller choices and the validated architecture/version facts. A regression should cover an unset JAVA_HOME with a successfully discovered JDK and then use that JDK for launch. I did not rerun the full real-provider verifier in this audit. Separately, actual scoped doctor calls on macOS arm64 showed an installed `openjdk@21` was missed automatically but worked with explicit JAVA_HOME; that discovery limitation is now #8504 and is distinct from this verifier handoff. Upstream research: Gradle's [toolchain inspection and launcher guidance](https://docs.gradle.org/current/userguide/toolchains.html) provides prior art for retaining detected runtime metadata through selection and execution. This is a design reference, not verification of REA or a proposal to auto-install JDKs.
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#4697
No description provided.