[GH-ISSUE #1189] [Bug] A truncated Mach-O header is admitted as a valid executable and then fails as a provider execution_failure #423

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

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

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

open_binary accepts a 12-byte prefix of a thin Mach-O executable as a valid arm64 target and binds Ghidra to it. The first provider query then fails as generic execution_failure with advice to retry or run doctor. This malformed target cannot contain a complete Mach-O header, and Ghidra correctly rejects its import. The target-admission boundary should reject the malformed file instead of reporting a later provider execution failure.

The MCP open returned success on both builds:

{
  "result": {
    "path": "/absolute/path/to/truncated",
    "format": "mach-o",
    "kind": "executable",
    "loaderArgs": [],
    "sha256": "28dec8e00af98c9b5120535e81997cf0423dab7421ec1ca2a6f0ff7f8e77b39e",
    "architecture": "arm64"
  }
}

The first list_documents query returned code: "execution_failure", category: "execution_failure", and "Analysis could not complete. Retry once; if it continues, run rea doctor." Its provider diagnostics retained these Ghidra messages:

No load spec found for import file: target-28dec8e00af9.bin
REPORT: Import failed for file: .../target-28dec8e00af9.bin
failure_kind: process
failure_message: Ghidra exited before bridge startup

binary_session still reported an open target. The CLI route likewise returned execution_failure and exited 1. A separate direct analyzeHeadless run with -readOnly -deleteProject -noanalysis also reported "No load spec found" and "Import failed" for the truncated input. The full fixture imported under the same direct invocation and a small post-script printed LIFECYCLE_ORACLE program=fixture memory=49152. The rejection is correct Ghidra behavior; the observed REA violation is the successful malformed admission and subsequent loss of the input-failure classification. parseThinMachO requires only eight bytes before returning executable metadata.

Reproduction

Use the baselines under Runtime or environment. From a disposable directory outside the REA checkout, configure the existing provider and generate a source-owned fixture:

export GHIDRA_INSTALL_DIR=/path/to/ghidra_12.1.4_PUBLIC
export JAVA_HOME=/path/to/jdk-21
export REA_MAIN=/path/to/rea-main
cat > fixture.c <<'EOF'
int main(void) { return 0; }
EOF
clang -g -O0 fixture.c -o fixture
head -c 12 fixture > truncated
npx -y rea-agents@6.1.0 analyze "$PWD/truncated" --provider ghidra --json
node "$REA_MAIN/scripts/rea.mjs" analyze "$PWD/truncated" --provider ghidra --json

For MCP, initialize each tested server separately with the same environment and run these supported requests on one connection:

{"name":"open_binary","arguments":{"path":"/absolute/path/to/truncated","provider_id":"ghidra"}}
{"name":"list_documents","arguments":{}}
{"name":"binary_session","arguments":{}}
{"name":"close_binary","arguments":{}}

Both CLI commands and both MCP sequences were executed. The full source-built executable is the control and opens and analyzes successfully. The observed truncated input begins with cffaedfe0c00000100000000; it has only the magic, CPU type, and CPU subtype fields, with the rest of the required header absent.

Expected behavior

Reject the truncated file during target admission with a malformed-input diagnostic that names the failed Mach-O header constraint. Do not bind a successful executable target or start Ghidra for this file. A subsequent query must not imply that a healthy provider failed on an admitted valid target. Preserve any previously open valid target when the malformed replacement is rejected.

Impact and scope

A bounded malformed input is advertised as a valid open executable and then produces a misleading provider failure, wasting an engine launch and directing users toward irrelevant provider repair. Normal valid input analysis remains available. Impact assessment: P2 (supported).

Workaround (available): Supply the complete executable instead of the truncated prefix. The full fixture was observed to open and analyze successfully through the same provider.

REA version

rea-agents@6.1.0 from npm, and main at b34bdcdb28 built locally with npm ci && npm run build:cached.

Runtime and platform

Tested against npm rea-agents@6.1.0 and the current main build at b34bdcdb2877be394c59c9cbf70269dc1841ff78, with Ghidra 12.1.4, Homebrew OpenJDK 21.0.12.1, Node 24.21.0, and macOS 27 arm64. The input was reduced to the first 12 bytes of a source-built thin arm64 Mach-O executable and reproduced at that size.

Target and artifact context

12-byte prefix of a thin arm64 Mach-O executable (Ghidra provider)

Interface

MCP

Earliest failure stage

Request parsing or validation

Relevant output or evidence

  • Expected source: src/contracts/sessionToolContracts.ts, open_binary description, "after validation"; docs/installation.md:511, "selects and validates the target/provider binding"; AGENTS.md, Boundary Contracts, paragraph distinguishing malformed input from unsupported targets and unavailable providers; docs/user-visible-errors.csv, AnalysisInputError and ProviderAdapterError trigger rows.
  • Failure source: The inline successful MCP open and subsequent failed list_documents result, plus the CLI commands above, observed on both tested builds. src/domain/binaryTarget.ts:50 through :64 accepts a thin Mach-O header after checking only eight bytes.
  • Evidence provenance: observed
  • Local verification: reproduced
  • Reproduction completeness: complete

Typed outcome or recovery expected

Repeat the exact 12-byte fixture through CLI and MCP. The open must reject the malformed header before any Ghidra launch, with an input-specific error and recovery advice to supply a complete artifact. With a valid fixture already open, reject this replacement while preserving the valid binding and its usable database. The full fixture must continue to import and analyze normally.

Analysis or suspected cause

No response

Additional context

No response

Originally created by @coygeek on GitHub (Oct 9, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/1189 ### 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 `open_binary` accepts a 12-byte prefix of a thin Mach-O executable as a valid arm64 target and binds Ghidra to it. The first provider query then fails as generic `execution_failure` with advice to retry or run doctor. This malformed target cannot contain a complete Mach-O header, and Ghidra correctly rejects its import. The target-admission boundary should reject the malformed file instead of reporting a later provider execution failure. The MCP open returned success on both builds: ```json { "result": { "path": "/absolute/path/to/truncated", "format": "mach-o", "kind": "executable", "loaderArgs": [], "sha256": "28dec8e00af98c9b5120535e81997cf0423dab7421ec1ca2a6f0ff7f8e77b39e", "architecture": "arm64" } } ``` The first `list_documents` query returned `code: "execution_failure"`, `category: "execution_failure"`, and "Analysis could not complete. Retry once; if it continues, run `rea doctor`." Its provider diagnostics retained these Ghidra messages: ```text No load spec found for import file: target-28dec8e00af9.bin REPORT: Import failed for file: .../target-28dec8e00af9.bin failure_kind: process failure_message: Ghidra exited before bridge startup ``` `binary_session` still reported an open target. The CLI route likewise returned `execution_failure` and exited 1. A separate direct `analyzeHeadless` run with `-readOnly -deleteProject -noanalysis` also reported "No load spec found" and "Import failed" for the truncated input. The full fixture imported under the same direct invocation and a small post-script printed `LIFECYCLE_ORACLE program=fixture memory=49152`. The rejection is correct Ghidra behavior; the observed REA violation is the successful malformed admission and subsequent loss of the input-failure classification. `parseThinMachO` requires only eight bytes before returning executable metadata. ### Reproduction Use the baselines under Runtime or environment. From a disposable directory outside the REA checkout, configure the existing provider and generate a source-owned fixture: ```sh export GHIDRA_INSTALL_DIR=/path/to/ghidra_12.1.4_PUBLIC export JAVA_HOME=/path/to/jdk-21 export REA_MAIN=/path/to/rea-main cat > fixture.c <<'EOF' int main(void) { return 0; } EOF clang -g -O0 fixture.c -o fixture head -c 12 fixture > truncated npx -y rea-agents@6.1.0 analyze "$PWD/truncated" --provider ghidra --json node "$REA_MAIN/scripts/rea.mjs" analyze "$PWD/truncated" --provider ghidra --json ``` For MCP, initialize each tested server separately with the same environment and run these supported requests on one connection: ```json {"name":"open_binary","arguments":{"path":"/absolute/path/to/truncated","provider_id":"ghidra"}} {"name":"list_documents","arguments":{}} {"name":"binary_session","arguments":{}} {"name":"close_binary","arguments":{}} ``` Both CLI commands and both MCP sequences were executed. The full source-built executable is the control and opens and analyzes successfully. The observed truncated input begins with `cffaedfe0c00000100000000`; it has only the magic, CPU type, and CPU subtype fields, with the rest of the required header absent. ### Expected behavior Reject the truncated file during target admission with a malformed-input diagnostic that names the failed Mach-O header constraint. Do not bind a successful executable target or start Ghidra for this file. A subsequent query must not imply that a healthy provider failed on an admitted valid target. Preserve any previously open valid target when the malformed replacement is rejected. ### Impact and scope A bounded malformed input is advertised as a valid open executable and then produces a misleading provider failure, wasting an engine launch and directing users toward irrelevant provider repair. Normal valid input analysis remains available. Impact assessment: P2 (supported). Workaround (available): Supply the complete executable instead of the truncated prefix. The full fixture was observed to open and analyze successfully through the same provider. ### REA version `rea-agents@6.1.0` from npm, and `main` at b34bdcdb2877be394c59c9cbf70269dc1841ff78 built locally with `npm ci && npm run build:cached`. ### Runtime and platform Tested against npm `rea-agents@6.1.0` and the current main build at `b34bdcdb2877be394c59c9cbf70269dc1841ff78`, with Ghidra 12.1.4, Homebrew OpenJDK 21.0.12.1, Node 24.21.0, and macOS 27 arm64. The input was reduced to the first 12 bytes of a source-built thin arm64 Mach-O executable and reproduced at that size. ### Target and artifact context 12-byte prefix of a thin arm64 Mach-O executable (Ghidra provider) ### Interface MCP ### Earliest failure stage Request parsing or validation ### Relevant output or evidence - Expected source: `src/contracts/sessionToolContracts.ts`, `open_binary` description, "after validation"; `docs/installation.md:511`, "selects and validates the target/provider binding"; `AGENTS.md`, Boundary Contracts, paragraph distinguishing malformed input from unsupported targets and unavailable providers; `docs/user-visible-errors.csv`, `AnalysisInputError` and `ProviderAdapterError` trigger rows. - Failure source: The inline successful MCP open and subsequent failed `list_documents` result, plus the CLI commands above, observed on both tested builds. `src/domain/binaryTarget.ts:50` through `:64` accepts a thin Mach-O header after checking only eight bytes. - Evidence provenance: observed - Local verification: reproduced - Reproduction completeness: complete ### Typed outcome or recovery expected Repeat the exact 12-byte fixture through CLI and MCP. The open must reject the malformed header before any Ghidra launch, with an input-specific error and recovery advice to supply a complete artifact. With a valid fixture already open, reject this replacement while preserving the valid binding and its usable database. The full fixture must continue to import and analyze normally. ### Analysis or suspected cause _No response_ ### Additional context _No response_
bsakaguchi 2026-10-15 18:24:55 +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#423
No description provided.