[GH-ISSUE #1219] [Bug] Ghidra provider aborts on every operation when the runtime root path contains a dot-prefixed element #429

Closed
opened 2026-10-15 18:24:58 +00:00 by bsakaguchi · 3 comments
Owner

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

Area

Provider (Ghidra)

Observed behavior

Every Ghidra-backed operation fails on an otherwise healthy installation when the
provider session root resolves under a directory whose name starts with ..
rea doctor reports the provider as configured and available:

ghidra                    ok=True  healthy /home/felipe/tools/ghidra_12.1.4_PUBLIC
ghidra-headless           ok=True  healthy .../support/analyzeHeadless
ghidra-native_decompiler  ok=True  healthy .../Decompiler/os/linux_x86_64/decompile
ghidra-java               ok=True  healthy 25.0.4.1; 64-bit; JDK
provider ghidra configured= True available= True version= 12.1.4

but the operation returns:

{ "error": "Analysis failed", "code": "execution_failure",
  "details": { "provider_id": "ghidra", "operation": "procedure_pseudo_code", "exit_code": 1 } }

with the real cause only inside details.diagnostics.stdout.text:

ERROR Abort due to Headless analyzer error: Path element starting with '.' is not permitted (HeadlessAnalyzer)
java.lang.IllegalArgumentException: Path element starting with '.' is not permitted
    at ghidra.util.NamingUtilities.checkName(NamingUtilities.java:108)
    at ghidra.framework.protocol.ghidra.GhidraURL.checkValidProjectPath(GhidraURL.java:440)
    at ghidra.framework.model.ProjectLocator.<init>(ProjectLocator.java:75)
    at ghidra.app.util.headless.HeadlessAnalyzer.processLocal(HeadlessAnalyzer.java:420)

Reproduction

No special target needed; any ELF works (/bin/true is enough).

export GHIDRA_INSTALL_DIR=/path/to/ghidra_12.1.4_PUBLIC
export TMPDIR=$HOME/.cache/rea-tmp        # any path with a dot-prefixed path element
mkdir -p "$TMPDIR"
npx -y rea-agents@6.1.0 decompile /bin/true main --provider ghidra --format json
# -> execution_failure; stdout carries the "Path element starting with '.' is not permitted" abort

Same command with a dot-free TMPDIR (e.g. /home/user/rea-tmp) succeeds.

Root cause

src/ghidra/GhidraClient.ts:

const SESSION_ROOT = tmpdir();          // line 51 — honours $TMPDIR
...
this.#runtimeRoot = await PrivateRuntimeRoot.create({
  parent: SESSION_ROOT,                 // line ~300
  prefix: "rea-ghidra-",
  platform: this.#options.platform,
});

The runtime root is <os.tmpdir()>/rea-ghidra-XXXX/, and project lives inside it,
so the Ghidra project path is <os.tmpdir()>/rea-ghidra-XXXX/project. Ghidra's
ProjectLocator runs every path element through NamingUtilities.checkName, which
rejects any element beginning with .. Nothing validates the session root against
that rule before launching the JVM, so the failure surfaces as a generic
execution_failure on the operation rather than a configuration diagnostic.

This is easy to hit in agent/sandbox runtimes that set TMPDIR to a hidden
directory (e.g. ~/.cache/<tool>, ~/.tmp), and it is not detectable through
rea doctor, which inspects the installation but not the runtime-root path.

Measured behavior (Ghidra 12.1.4, Linux x64, JDK 25, rea-agents 6.1.0)

TMPDIR result
/home/<user>/.hermes/cache/scratch (dot-prefixed element) abort: Path element starting with '.' is not permitted
/home/<user>/tools/rea-tmp (dot-free) success, procedure_pseudo_code returned
/home/<user>/tools/rea.tmp (dot inside an element) success — only a leading . is rejected

Suggested direction (open to a different one)

Choose the runtime-root parent through a helper that validates it against Ghidra's
project-path rule and relocates when it is not usable — preferred parent when safe,
otherwise a dot-free POSIX temp directory (or the nearest safe ancestor) — and report
the chosen root plus the reason in the session/diagnostics so this failure is not
reported as "analysis could not complete".

I have a local patch for this (a ghidraSessionRoot helper plus the GhidraClient wiring and
7 unit tests covering the inherited base, interior dots, a hidden base, a dotted fallback, the
nearest safe ancestor and the default platform temp directory), with the fast vitest lane,
tsc --noEmit and docs:check all green. Happy to open a PR once the fallback direction is agreed.


Verified with rea-agents 6.1.0, Ghidra 12.1.4 (linux_x86_64), JDK 25, Ubuntu 26.04. The failing run and the succeeding run are both reproducible from the commands above.

Originally created by @felipeatp on GitHub (Oct 9, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/1219 ## Area Provider (Ghidra) ## Observed behavior Every Ghidra-backed operation fails on an otherwise healthy installation when the provider session root resolves under a directory whose name starts with `.`. `rea doctor` reports the provider as configured and available: ``` ghidra ok=True healthy /home/felipe/tools/ghidra_12.1.4_PUBLIC ghidra-headless ok=True healthy .../support/analyzeHeadless ghidra-native_decompiler ok=True healthy .../Decompiler/os/linux_x86_64/decompile ghidra-java ok=True healthy 25.0.4.1; 64-bit; JDK provider ghidra configured= True available= True version= 12.1.4 ``` but the operation returns: ```json { "error": "Analysis failed", "code": "execution_failure", "details": { "provider_id": "ghidra", "operation": "procedure_pseudo_code", "exit_code": 1 } } ``` with the real cause only inside `details.diagnostics.stdout.text`: ``` ERROR Abort due to Headless analyzer error: Path element starting with '.' is not permitted (HeadlessAnalyzer) java.lang.IllegalArgumentException: Path element starting with '.' is not permitted at ghidra.util.NamingUtilities.checkName(NamingUtilities.java:108) at ghidra.framework.protocol.ghidra.GhidraURL.checkValidProjectPath(GhidraURL.java:440) at ghidra.framework.model.ProjectLocator.<init>(ProjectLocator.java:75) at ghidra.app.util.headless.HeadlessAnalyzer.processLocal(HeadlessAnalyzer.java:420) ``` ## Reproduction No special target needed; any ELF works (`/bin/true` is enough). ```bash export GHIDRA_INSTALL_DIR=/path/to/ghidra_12.1.4_PUBLIC export TMPDIR=$HOME/.cache/rea-tmp # any path with a dot-prefixed path element mkdir -p "$TMPDIR" npx -y rea-agents@6.1.0 decompile /bin/true main --provider ghidra --format json # -> execution_failure; stdout carries the "Path element starting with '.' is not permitted" abort ``` Same command with a dot-free `TMPDIR` (e.g. `/home/user/rea-tmp`) succeeds. ## Root cause `src/ghidra/GhidraClient.ts`: ```ts const SESSION_ROOT = tmpdir(); // line 51 — honours $TMPDIR ... this.#runtimeRoot = await PrivateRuntimeRoot.create({ parent: SESSION_ROOT, // line ~300 prefix: "rea-ghidra-", platform: this.#options.platform, }); ``` The runtime root is `<os.tmpdir()>/rea-ghidra-XXXX/`, and `project` lives inside it, so the Ghidra project path is `<os.tmpdir()>/rea-ghidra-XXXX/project`. Ghidra's `ProjectLocator` runs every path element through `NamingUtilities.checkName`, which rejects any element beginning with `.`. Nothing validates the session root against that rule before launching the JVM, so the failure surfaces as a generic `execution_failure` on the operation rather than a configuration diagnostic. This is easy to hit in agent/sandbox runtimes that set `TMPDIR` to a hidden directory (e.g. `~/.cache/<tool>`, `~/.tmp`), and it is not detectable through `rea doctor`, which inspects the installation but not the runtime-root path. ## Measured behavior (Ghidra 12.1.4, Linux x64, JDK 25, rea-agents 6.1.0) | `TMPDIR` | result | | --- | --- | | `/home/<user>/.hermes/cache/scratch` (dot-prefixed element) | abort: `Path element starting with '.' is not permitted` | | `/home/<user>/tools/rea-tmp` (dot-free) | success, `procedure_pseudo_code` returned | | `/home/<user>/tools/rea.tmp` (dot **inside** an element) | success — only a leading `.` is rejected | ## Suggested direction (open to a different one) Choose the runtime-root parent through a helper that validates it against Ghidra's project-path rule and relocates when it is not usable — preferred parent when safe, otherwise a dot-free POSIX temp directory (or the nearest safe ancestor) — and report the chosen root plus the reason in the session/diagnostics so this failure is not reported as "analysis could not complete". I have a local patch for this (a `ghidraSessionRoot` helper plus the `GhidraClient` wiring and 7 unit tests covering the inherited base, interior dots, a hidden base, a dotted fallback, the nearest safe ancestor and the default platform temp directory), with the fast vitest lane, `tsc --noEmit` and `docs:check` all green. Happy to open a PR once the fallback direction is agreed. --- Verified with `rea-agents` 6.1.0, Ghidra 12.1.4 (linux_x86_64), JDK 25, Ubuntu 26.04. The failing run and the succeeding run are both reproducible from the commands above.
bsakaguchi 2026-10-15 18:24:58 +00:00
Author
Owner

@felipeatp commented on GitHub (Oct 9, 2026):

The patch described above is already written and verified, and it is pushed to my fork if you want to look before deciding a direction: https://github.com/felipeatp/rea/tree/fix/ghidra-session-root

It adds a ghidraSessionRoot helper plus the GhidraClient wiring and 7 unit tests (inherited base kept, interior dot kept, hidden base relocated, dotted fallback ignored, nearest safe ancestor, POSIX default). With it applied: npx vitest run --project adapters src/ghidra/GhidraSessionRoot.test.ts 7 passed, the fast lane 3,427 passed, npx tsc --noEmit exit 0 and npm run docs:check 2 tasks successful.

Two things are open on purpose: the fallback directory (I default to the platform temp directory on POSIX, then the nearest dot-free ancestor) and whether the chosen root should be reported in session/diagnostics. Say the word and I open it as a PR, or adapt it to whichever direction you prefer.

<!-- gh-comment-id:6076565798 --> @felipeatp commented on GitHub (Oct 9, 2026): The patch described above is already written and verified, and it is pushed to my fork if you want to look before deciding a direction: https://github.com/felipeatp/rea/tree/fix/ghidra-session-root It adds a `ghidraSessionRoot` helper plus the `GhidraClient` wiring and 7 unit tests (inherited base kept, interior dot kept, hidden base relocated, dotted fallback ignored, nearest safe ancestor, POSIX default). With it applied: `npx vitest run --project adapters src/ghidra/GhidraSessionRoot.test.ts` 7 passed, the fast lane 3,427 passed, `npx tsc --noEmit` exit 0 and `npm run docs:check` 2 tasks successful. Two things are open on purpose: the fallback directory (I default to the platform temp directory on POSIX, then the nearest dot-free ancestor) and whether the chosen root should be reported in session/diagnostics. Say the word and I open it as a PR, or adapt it to whichever direction you prefer.
Author
Owner

@KungFuryPO commented on GitHub (Oct 9, 2026):

Confirmed independently on 6.0.0 as well.

Environment: rea-agents 6.0.0 (pinned) | Ghidra 12.1.2 release build + JDK 21 | Ubuntu. rea doctor reported the Ghidra provider as configured and available, yet every Ghidra-backed operation failed while the provider session root resolved under a dot-prefixed path element — reproduced across analyze, function, search, read-bytes and inspect-native-load-image, on both MZ and headerless DOS targets. The abort appears only inside details.diagnostics.stdout.text:

ERROR Abort due to Headless analyzer error: Path element starting with '.' is not permitted (HeadlessAnalyzer)

Pointing TMPDIR at a dot-free path resolved every operation immediately.

Two suggestions, matching the existing analysis: validate the resolved session root against Ghidra's naming rules before launching the JVM, and surface the captured abort line in the top-level failure message so the generic "retry once" hint does not mask the real cause.

<!-- gh-comment-id:6081624656 --> @KungFuryPO commented on GitHub (Oct 9, 2026): Confirmed independently on **6.0.0** as well. Environment: rea-agents 6.0.0 (pinned) | Ghidra 12.1.2 release build + JDK 21 | Ubuntu. `rea doctor` reported the Ghidra provider as configured and available, yet every Ghidra-backed operation failed while the provider session root resolved under a dot-prefixed path element — reproduced across `analyze`, `function`, `search`, `read-bytes` and `inspect-native-load-image`, on both MZ and headerless DOS targets. The abort appears only inside `details.diagnostics.stdout.text`: ``` ERROR Abort due to Headless analyzer error: Path element starting with '.' is not permitted (HeadlessAnalyzer) ``` Pointing `TMPDIR` at a dot-free path resolved every operation immediately. Two suggestions, matching the existing analysis: validate the resolved session root against Ghidra's naming rules before launching the JVM, and surface the captured abort line in the top-level failure message so the generic "retry once" hint does not mask the real cause.
Author
Owner

@ylwl1997 commented on GitHub (Oct 10, 2026):

Leaving a short FAQ-style note for anyone who still hits this after the issue closed — the failure mode is easy to misread because doctor looks healthy.

Symptom: Every Ghidra-backed op returns execution_failure / exit 1, while ghidra / ghidra-headless / ghidra-java all report ok=True. The real abort is buried in details.diagnostics.stdout.text:

Path element starting with '.' is not permitted (HeadlessAnalyzer)

Cause: The provider session root (often via TMPDIR / TEMP / a cache under ~/.…) contains a path segment that starts with .. Ghidra headless rejects that project location; install checks never see it.

Fix checklist:

  1. Point temp at a path without dot-prefixed segments, e.g. /var/tmp/rea-work or $HOME/rea-tmp (not $HOME/.cache/…).
  2. Export it in the same shell/MCP launcher that runs REA:
    export TMPDIR=/var/tmp/rea-work
    mkdir -p "$TMPDIR"
    
  3. Re-run doctor --provider ghidra, then one tiny binary (/bin/true is enough) end-to-end.
  4. If you use systemd/GUI-launched clients, set the env there too — a fixed interactive shell does not help the MCP process.

Thanks to the OP for the clear repro and to the independent confirmation on 6.0.0.

Disclosure: I maintain rea.run, an independent bilingual docs/FAQ site about lawful coding-agent reverse engineering. Not affiliated with the REA project.

<!-- gh-comment-id:6095660063 --> @ylwl1997 commented on GitHub (Oct 10, 2026): Leaving a short FAQ-style note for anyone who still hits this after the issue closed — the failure mode is easy to misread because `doctor` looks healthy. **Symptom:** Every Ghidra-backed op returns `execution_failure` / exit 1, while `ghidra` / `ghidra-headless` / `ghidra-java` all report `ok=True`. The real abort is buried in `details.diagnostics.stdout.text`: `Path element starting with '.' is not permitted (HeadlessAnalyzer)` **Cause:** The provider session root (often via `TMPDIR` / `TEMP` / a cache under `~/.…`) contains a path segment that starts with `.`. Ghidra headless rejects that project location; install checks never see it. **Fix checklist:** 1. Point temp at a path **without** dot-prefixed segments, e.g. `/var/tmp/rea-work` or `$HOME/rea-tmp` (not `$HOME/.cache/…`). 2. Export it in the **same** shell/MCP launcher that runs REA: ```bash export TMPDIR=/var/tmp/rea-work mkdir -p "$TMPDIR" ``` 3. Re-run `doctor --provider ghidra`, then one tiny binary (`/bin/true` is enough) end-to-end. 4. If you use systemd/GUI-launched clients, set the env there too — a fixed interactive shell does not help the MCP process. Thanks to the OP for the clear repro and to the independent confirmation on 6.0.0. Disclosure: I maintain [rea.run](https://rea.run/faq/tmpdir-dot-path/), an independent bilingual docs/FAQ site about lawful coding-agent reverse engineering. Not affiliated with the REA project.
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#429
No description provided.