[GH-ISSUE #5680] [Bug] inspect-macho rejects valid section and segment names equal to version #3431

Closed
opened 2026-10-31 00:40:24 +00:00 by bsakaguchi · 0 comments
Owner

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

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

inspect-macho rejects linker-generated thin arm64 Mach-O executables when a section or segment is named exactly version. Both cases exit 1 with unreadable_output and Malformed otool load-command output, although the captured otool invocation exits 0 and contains the legal name. Changing only the section name to __version succeeds.

Reproduction

From the source checkout with locked dependencies installed and npm run build:cached completed, on macOS arm64 with Apple clang and otool available:

work_dir="$(mktemp -d)"
mkdir -p "$work_dir"/{home,cfg,data,cache,state,tmp,fixtures}
printf 'int main(void){return 0;}\n' > "$work_dir/fixtures/main.c"
printf x > "$work_dir/fixtures/data.bin"
clang -arch arm64 -o "$work_dir/fixtures/section-version" \
  "$work_dir/fixtures/main.c" \
  -Wl,-sectcreate,__DATA,version,"$work_dir/fixtures/data.bin"
clang -arch arm64 -o "$work_dir/fixtures/segment-version" \
  "$work_dir/fixtures/main.c" \
  -Wl,-sectcreate,version,__mysect,"$work_dir/fixtures/data.bin"
clang -arch arm64 -o "$work_dir/fixtures/control" \
  "$work_dir/fixtures/main.c" \
  -Wl,-sectcreate,__DATA,__version,"$work_dir/fixtures/data.bin"
for fixture in section-version segment-version control; do
  otool -l "$work_dir/fixtures/$fixture" > "$work_dir/$fixture.otool.txt"
  env -i PATH="$PATH" HOME="$work_dir/home" \
    XDG_CONFIG_HOME="$work_dir/cfg" XDG_DATA_HOME="$work_dir/data" \
    XDG_CACHE_HOME="$work_dir/cache" XDG_STATE_HOME="$work_dir/state" \
    TMPDIR="$work_dir/tmp" REA_LOG_LEVEL=silent \
    node scripts/rea.mjs inspect-macho "$work_dir/fixtures/$fixture" --json \
    > "$work_dir/$fixture.json" 2> "$work_dir/$fixture.stderr"
  result_code=$?
  printf '%s exit=%s\n' "$fixture" "$result_code"
done

Run the loop without shell errexit so it continues after expected failures. These fixtures were inspected without executing them. The independent verifier reproduced each case twice; the submitter freshly compiled and reran all three once with the same results.

Expected behavior

Accept these linker-generated inputs and return the section or segment name version. Retain the successful __version control. The native inspection contract advertises segments, sections and load commands, and the native investigation guide promises original raw load-command values.

Impact and scope

P2, supported: two bounded legal inputs lose the entire inspection result. Reproduction frequency was three out of three invocations for each affected fixture across verifier and submitter runs. Real-world frequency and other architectures are unknown. The independent verifier observed unchanged input files; no fixture execution or security-boundary crossing occurred.

Direct otool successfully exposes the names, but does not return REA's normalized Evidence. An equivalent REA workaround was not verified; alternate routes were not exhaustively tested.

REA version

rea-agents 6.7.0; reproduced source commit 9d8e7803326a382cbbb3ffdb900f5e93e5442d68. Fetched main at 0bc2b42001f684beffcf47b70e507443b1a5d320: the cited parser, inspection/error-handling, contract and guide files are unchanged. The fresh CLI replay used the verified baseline runtime; no separate main runtime was built.

Runtime and platform

Node.js v26.12.0; macOS 27.0.1; arm64; Apple clang 21.0.0 (clang-2100.3.34.2). Hopper/Ghidra were not involved.

Target and artifact context

Self-built thin arm64 Mach-O executables from the inline C source and one-byte data file. The section fixture uses segment __DATA, section version; the segment fixture uses segment version, section __mysect; the control uses segment __DATA, section __version.

Interface

CLI

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

Observed exits:

section-version exit=1
segment-version exit=1
control exit=0

Each rejected result contains this JSON subset, with path-bearing captures omitted:

{
  "code": "unreadable_output",
  "category": "execution_failure",
  "details": {"reason": "Malformed otool load-command output"}
}

Independent otool -l output and REA's successful-tool capture contain sectname version and segname version, respectively. The successful control includes a section with name: "__version" under normalized_result.segments.items[].sections.items[].

Typed outcome or recovery expected

Both affected commands should exit 0 and preserve their exact section/segment names. Keep meaningful compound labels current version, compatibility version and time stamp, and retain rejection of genuinely missing or repeated required fields. These are proposed restoration checks; no fix was tested.

Analysis or suspected cause

Source-derived reasoning: parseField consumes version after any first token into the field label. Thus sectname version and segname version become invented labels with empty values. Required-field checks then see the actual required label as missing. NativeMachoInspection.ts:160-180 translates the parser failure into the observed output error. The defect is in REA's parser, not a bundled dependency.

Additional context

Partial overlap with open PR #4979, linked to #3384: that PR explicitly handles name and path rows, including a version prefix. This report is narrowed to section/segment rows, which its diff leaves on the generic parser. The dylib and rpath manifestations are excluded here.

Merged PR #534 narrowed compound time stamp matching but explicitly retained generic token version matching; these fixtures still fail with that change in the baseline. This is not the valueless/truncated-output input fixed by #1995. No earlier known-good version was established.

Originally created by @coygeek on GitHub (Oct 20, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/5680 ### 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 `inspect-macho` rejects linker-generated thin arm64 Mach-O executables when a section or segment is named exactly `version`. Both cases exit 1 with `unreadable_output` and `Malformed otool load-command output`, although the captured `otool` invocation exits 0 and contains the legal name. Changing only the section name to `__version` succeeds. ### Reproduction From the source checkout with locked dependencies installed and `npm run build:cached` completed, on macOS arm64 with Apple clang and otool available: ```sh work_dir="$(mktemp -d)" mkdir -p "$work_dir"/{home,cfg,data,cache,state,tmp,fixtures} printf 'int main(void){return 0;}\n' > "$work_dir/fixtures/main.c" printf x > "$work_dir/fixtures/data.bin" clang -arch arm64 -o "$work_dir/fixtures/section-version" \ "$work_dir/fixtures/main.c" \ -Wl,-sectcreate,__DATA,version,"$work_dir/fixtures/data.bin" clang -arch arm64 -o "$work_dir/fixtures/segment-version" \ "$work_dir/fixtures/main.c" \ -Wl,-sectcreate,version,__mysect,"$work_dir/fixtures/data.bin" clang -arch arm64 -o "$work_dir/fixtures/control" \ "$work_dir/fixtures/main.c" \ -Wl,-sectcreate,__DATA,__version,"$work_dir/fixtures/data.bin" for fixture in section-version segment-version control; do otool -l "$work_dir/fixtures/$fixture" > "$work_dir/$fixture.otool.txt" env -i PATH="$PATH" HOME="$work_dir/home" \ XDG_CONFIG_HOME="$work_dir/cfg" XDG_DATA_HOME="$work_dir/data" \ XDG_CACHE_HOME="$work_dir/cache" XDG_STATE_HOME="$work_dir/state" \ TMPDIR="$work_dir/tmp" REA_LOG_LEVEL=silent \ node scripts/rea.mjs inspect-macho "$work_dir/fixtures/$fixture" --json \ > "$work_dir/$fixture.json" 2> "$work_dir/$fixture.stderr" result_code=$? printf '%s exit=%s\n' "$fixture" "$result_code" done ``` Run the loop without shell `errexit` so it continues after expected failures. These fixtures were inspected without executing them. The independent verifier reproduced each case twice; the submitter freshly compiled and reran all three once with the same results. ### Expected behavior Accept these linker-generated inputs and return the section or segment name `version`. Retain the successful `__version` control. The [native inspection contract](https://github.com/morluto/rea/blob/9d8e7803326a382cbbb3ffdb900f5e93e5442d68/src/contracts/native/nativeToolContracts.ts#L95-L103) advertises segments, sections and load commands, and [the native investigation guide](https://github.com/morluto/rea/blob/9d8e7803326a382cbbb3ffdb900f5e93e5442d68/docs/native-investigation.md#L311-L322) promises original raw load-command values. ### Impact and scope P2, supported: two bounded legal inputs lose the entire inspection result. Reproduction frequency was three out of three invocations for each affected fixture across verifier and submitter runs. Real-world frequency and other architectures are unknown. The independent verifier observed unchanged input files; no fixture execution or security-boundary crossing occurred. Direct `otool` successfully exposes the names, but does not return REA's normalized Evidence. An equivalent REA workaround was not verified; alternate routes were not exhaustively tested. ### REA version `rea-agents` 6.7.0; reproduced source commit `9d8e7803326a382cbbb3ffdb900f5e93e5442d68`. Fetched main at `0bc2b42001f684beffcf47b70e507443b1a5d320`: the cited parser, inspection/error-handling, contract and guide files are unchanged. The fresh CLI replay used the verified baseline runtime; no separate main runtime was built. ### Runtime and platform Node.js v26.12.0; macOS 27.0.1; arm64; Apple clang 21.0.0 (clang-2100.3.34.2). Hopper/Ghidra were not involved. ### Target and artifact context Self-built thin arm64 Mach-O executables from the inline C source and one-byte data file. The section fixture uses segment `__DATA`, section `version`; the segment fixture uses segment `version`, section `__mysect`; the control uses segment `__DATA`, section `__version`. ### Interface CLI ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence Observed exits: ```text section-version exit=1 segment-version exit=1 control exit=0 ``` Each rejected result contains this JSON subset, with path-bearing captures omitted: ```json { "code": "unreadable_output", "category": "execution_failure", "details": {"reason": "Malformed otool load-command output"} } ``` Independent `otool -l` output and REA's successful-tool capture contain `sectname version` and `segname version`, respectively. The successful control includes a section with `name: "__version"` under `normalized_result.segments.items[].sections.items[]`. ### Typed outcome or recovery expected Both affected commands should exit 0 and preserve their exact section/segment names. Keep meaningful compound labels `current version`, `compatibility version` and `time stamp`, and retain rejection of genuinely missing or repeated required fields. These are proposed restoration checks; no fix was tested. ### Analysis or suspected cause Source-derived reasoning: [`parseField`](https://github.com/morluto/rea/blob/9d8e7803326a382cbbb3ffdb900f5e93e5442d68/src/native/parsers/otool.ts#L480-L490) consumes `version` after any first token into the field label. Thus `sectname version` and `segname version` become invented labels with empty values. Required-field checks then see the actual required label as missing. `NativeMachoInspection.ts:160-180` translates the parser failure into the observed output error. The defect is in REA's parser, not a bundled dependency. ### Additional context Partial overlap with open PR [#4979](https://github.com/morluto/rea/pull/4979), linked to [#3384](https://github.com/morluto/rea/issues/3384): that PR explicitly handles `name` and `path` rows, including a `version` prefix. This report is narrowed to section/segment rows, which its diff leaves on the generic parser. The dylib and rpath manifestations are excluded here. Merged PR [#534](https://github.com/morluto/rea/pull/534) narrowed compound `time stamp` matching but explicitly retained generic `token version` matching; these fixtures still fail with that change in the baseline. This is not the valueless/truncated-output input fixed by [#1995](https://github.com/morluto/rea/pull/1995). No earlier known-good version was established.
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#3431
No description provided.