[GH-ISSUE #5713] [Bug] Native target admission rejects valid thin arm64_32 Mach-O files #3461

Closed
opened 2026-10-31 00:40:43 +00:00 by bsakaguchi · 1 comment
Owner

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

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

list-architectures, inspect-macho and inspect-signature each reject a compiler-produced thin arm64_32 Mach-O object at target admission with target_unavailable / unsupported_target. The same object bytes are successfully inventoried as an arm64_32 slice when packaged beside an arm64 object in a universal container.

Reproduction

From a source checkout with locked dependencies installed and npm run build:cached completed, on an arm64 Mac with Apple clang, lipo, otool and jq:

work_dir="$(mktemp -d)"
S="$work_dir"
mkdir -p "$S/home" "$S/cfg" "$S/data" "$S/cache" "$S/state" "$S/tmp"
printf 'int f(void){return 42;}\n' > "$S/f.c"
clang -arch arm64_32 -c "$S/f.c" -o "$S/arm64_32.o"
clang -arch arm64 -c "$S/f.c" -o "$S/arm64.o"
lipo -create "$S/arm64.o" "$S/arm64_32.o" -output "$S/fat.o"

file "$S/arm64_32.o"
lipo -detailed_info "$S/arm64_32.o"
otool -hv "$S/arm64_32.o"

for target in arm64_32.o arm64.o fat.o; do
  for command in list-architectures inspect-macho inspect-signature; do
    env -i PATH="$PATH" HOME="$S/home" XDG_CONFIG_HOME="$S/cfg" \
      XDG_DATA_HOME="$S/data" XDG_CACHE_HOME="$S/cache" \
      XDG_STATE_HOME="$S/state" TMPDIR="$S/tmp" REA_LOG_LEVEL=silent \
      node scripts/rea.mjs "$command" "$S/$target" --json \
      > "$S/$target.$command.json" 2> "$S/$target.$command.stderr"
    printf '%s %s exit %s\n' "$target" "$command" "$?"
  done
done

jq '{code, category, message, reason: .details.reason}' \
  "$S/arm64_32.o.list-architectures.json"
jq '.normalized_result.architectures' "$S/fat.o.list-architectures.json"

The independent verifier ran all nine CLI combinations at the baseline below. The submitter rebuilt current main and repeated the same fixture construction and nine commands through an equivalent isolated runner. On each revision, all three thin arm64_32 calls exited 1, and all six controls exited 0. No target was executed.

Expected behavior

list-architectures should identify the thin arm64_32 object and return ordinary thin-file Evidence with native-tool provenance. Preserve existing thin-file metadata conventions; this report does not require the CPU-code, offset, size and alignment fields available for FAT rows to become populated for thin files.

The list_architectures contract promises thin or universal Mach-O slice inventory. Native investigation documentation describes thin and universal coordinates. The public help accepts a Mach-O or app path. The successful universal control directly establishes recognition of this architecture and these bytes for inventory.

Impact and scope

P2: a bounded valid-input family cannot obtain normalized static inspection Evidence through the three tested commands. Apple lipo and otool recover architecture and section facts; REA also inventories the exact slice inside the mixed FAT control. These are verified partial workarounds, without establishing full thin-arm64_32 normalized inspection. Linked watchOS applications, other commands, deep-analysis providers and MCP were not tested. No security boundary was crossed.

REA version

rea-agents 6.7.0, baseline source 9d8e7803326a382cbbb3ffdb900f5e93e5442d68. Rechecked after fetching and building origin/main at 84c89d1107cda017509bc3cd851573d073f9906d, also 6.7.0; the same refusal remains.

Runtime and platform

Node.js v26.12.0; macOS 27.0.1; arm64; Apple clang 21.0.0, clang-2100.3.34.2. Native Apple inspection utilities; no Hopper or Ghidra.

Target and artifact context

Compiler-produced unsigned relocatable Mach-O objects built from int f(void){return 42;} for arm64_32 and arm64, plus their lipo-created universal container. This is an object-file test, not a linked watchOS application or a watchOS runtime-validity claim.

Interface

CLI

Earliest failure stage

Setup or provider admission

Relevant output or evidence

Each thin arm64_32 command exits 1 with this JSON subset on both revisions:

{
  "code": "target_unavailable",
  "category": "unsupported_target",
  "message": "Cannot open artifact: unsupported Mach-O architecture.",
  "details": {
    "reason": "unsupported Mach-O architecture",
    "constraint": "file_format_rejected"
  }
}

The successful FAT inventory includes:

{
  "name": "arm64_32",
  "cpu_type": "CPU_TYPE_ARM64_32",
  "cpu_subtype": "CPU_SUBTYPE_ARM64_32_V8",
  "file_offset": 552,
  "size": 436,
  "alignment": 8
}

An independent FAT-table read compared that byte range with the rejected thin object and returned byte_equal=True. Apple file identified it as a Mach-O arm64_32_v8 object; lipo -detailed_info identified arm64_32; otool -hv reported MH_MAGIC ARM64_32 V8 OBJECT. The verifier also observed two sections, __text and __compact_unwind. Its codesign -dv check exited 1 with the ordinary unsigned-object diagnostic, not a signing success. Evidence provenance: observed; local verification: independently reproduced and replayed on current main.

Typed outcome or recovery expected

Return successful thin architecture inventory. Proposed related restoration checks: inspect-macho returns the object's 32-bit, little-endian header and two sections; inspect-signature returns its ordinary unsigned state. Preserve the successful arm64 and FAT controls and typed refusals for invalid inputs. No repair or restoration was tested.

Analysis or suspected cause

Source-derived reasoning: parseThinMachO rejects CPU types that machArchitecture cannot map. Its switch omits 0x0200000c. FAT admission skips the unmapped family and selects arm64 in the control, allowing native inventory to report both slices. These admission paths are unchanged at the rechecked main revision. This is REA's admission logic, not an identified bundled dependency failure.

Additional context

Prepared with AI assistance and independently verified against real Apple tools. No earlier known-good version was established.

PR #5359 explicitly notes the thin arm64_32 admission refusal as an existing limitation and tests that architecture inside FAT; its encryption-reporting change does not repair admission. Issue #5404 and PR #5501 concern universal containers with no host-compatible slice, whereas this report concerns the thin-file CPU check. Closed #3389 concerns exact arm64e.x1 FAT naming. Those have different restoration signals.

Originally created by @coygeek on GitHub (Oct 20, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/5713 ### 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 `list-architectures`, `inspect-macho` and `inspect-signature` each reject a compiler-produced thin `arm64_32` Mach-O object at target admission with `target_unavailable` / `unsupported_target`. The same object bytes are successfully inventoried as an `arm64_32` slice when packaged beside an arm64 object in a universal container. ### Reproduction From a source checkout with locked dependencies installed and `npm run build:cached` completed, on an arm64 Mac with Apple clang, lipo, otool and jq: ```sh work_dir="$(mktemp -d)" S="$work_dir" mkdir -p "$S/home" "$S/cfg" "$S/data" "$S/cache" "$S/state" "$S/tmp" printf 'int f(void){return 42;}\n' > "$S/f.c" clang -arch arm64_32 -c "$S/f.c" -o "$S/arm64_32.o" clang -arch arm64 -c "$S/f.c" -o "$S/arm64.o" lipo -create "$S/arm64.o" "$S/arm64_32.o" -output "$S/fat.o" file "$S/arm64_32.o" lipo -detailed_info "$S/arm64_32.o" otool -hv "$S/arm64_32.o" for target in arm64_32.o arm64.o fat.o; do for command in list-architectures inspect-macho inspect-signature; do env -i PATH="$PATH" HOME="$S/home" XDG_CONFIG_HOME="$S/cfg" \ XDG_DATA_HOME="$S/data" XDG_CACHE_HOME="$S/cache" \ XDG_STATE_HOME="$S/state" TMPDIR="$S/tmp" REA_LOG_LEVEL=silent \ node scripts/rea.mjs "$command" "$S/$target" --json \ > "$S/$target.$command.json" 2> "$S/$target.$command.stderr" printf '%s %s exit %s\n' "$target" "$command" "$?" done done jq '{code, category, message, reason: .details.reason}' \ "$S/arm64_32.o.list-architectures.json" jq '.normalized_result.architectures' "$S/fat.o.list-architectures.json" ``` The independent verifier ran all nine CLI combinations at the baseline below. The submitter rebuilt current main and repeated the same fixture construction and nine commands through an equivalent isolated runner. On each revision, all three thin arm64_32 calls exited 1, and all six controls exited 0. No target was executed. ### Expected behavior `list-architectures` should identify the thin arm64_32 object and return ordinary thin-file Evidence with native-tool provenance. Preserve existing thin-file metadata conventions; this report does not require the CPU-code, offset, size and alignment fields available for FAT rows to become populated for thin files. The [list_architectures contract](https://github.com/morluto/rea/blob/9d8e7803326a382cbbb3ffdb900f5e93e5442d68/src/contracts/native/nativeToolContracts.ts#L119-L121) promises thin or universal Mach-O slice inventory. [Native investigation documentation](https://github.com/morluto/rea/blob/9d8e7803326a382cbbb3ffdb900f5e93e5442d68/docs/native-investigation.md#L311-L326) describes thin and universal coordinates. The public help accepts a Mach-O or app path. The successful universal control directly establishes recognition of this architecture and these bytes for inventory. ### Impact and scope P2: a bounded valid-input family cannot obtain normalized static inspection Evidence through the three tested commands. Apple lipo and otool recover architecture and section facts; REA also inventories the exact slice inside the mixed FAT control. These are verified partial workarounds, without establishing full thin-arm64_32 normalized inspection. Linked watchOS applications, other commands, deep-analysis providers and MCP were not tested. No security boundary was crossed. ### REA version `rea-agents` 6.7.0, baseline source `9d8e7803326a382cbbb3ffdb900f5e93e5442d68`. Rechecked after fetching and building `origin/main` at `84c89d1107cda017509bc3cd851573d073f9906d`, also 6.7.0; the same refusal remains. ### Runtime and platform Node.js v26.12.0; macOS 27.0.1; arm64; Apple clang 21.0.0, clang-2100.3.34.2. Native Apple inspection utilities; no Hopper or Ghidra. ### Target and artifact context Compiler-produced unsigned relocatable Mach-O objects built from `int f(void){return 42;}` for arm64_32 and arm64, plus their lipo-created universal container. This is an object-file test, not a linked watchOS application or a watchOS runtime-validity claim. ### Interface CLI ### Earliest failure stage Setup or provider admission ### Relevant output or evidence Each thin arm64_32 command exits 1 with this JSON subset on both revisions: ```json { "code": "target_unavailable", "category": "unsupported_target", "message": "Cannot open artifact: unsupported Mach-O architecture.", "details": { "reason": "unsupported Mach-O architecture", "constraint": "file_format_rejected" } } ``` The successful FAT inventory includes: ```json { "name": "arm64_32", "cpu_type": "CPU_TYPE_ARM64_32", "cpu_subtype": "CPU_SUBTYPE_ARM64_32_V8", "file_offset": 552, "size": 436, "alignment": 8 } ``` An independent FAT-table read compared that byte range with the rejected thin object and returned `byte_equal=True`. Apple `file` identified it as a Mach-O arm64_32_v8 object; `lipo -detailed_info` identified arm64_32; `otool -hv` reported `MH_MAGIC ARM64_32 V8 OBJECT`. The verifier also observed two sections, `__text` and `__compact_unwind`. Its `codesign -dv` check exited 1 with the ordinary unsigned-object diagnostic, not a signing success. Evidence provenance: observed; local verification: independently reproduced and replayed on current main. ### Typed outcome or recovery expected Return successful thin architecture inventory. Proposed related restoration checks: `inspect-macho` returns the object's 32-bit, little-endian header and two sections; `inspect-signature` returns its ordinary unsigned state. Preserve the successful arm64 and FAT controls and typed refusals for invalid inputs. No repair or restoration was tested. ### Analysis or suspected cause Source-derived reasoning: [parseThinMachO](https://github.com/morluto/rea/blob/9d8e7803326a382cbbb3ffdb900f5e93e5442d68/src/domain/binaryTarget.ts#L118-L124) rejects CPU types that `machArchitecture` cannot map. Its [switch](https://github.com/morluto/rea/blob/9d8e7803326a382cbbb3ffdb900f5e93e5442d68/src/domain/binaryTarget.ts#L610-L625) omits `0x0200000c`. FAT admission skips the unmapped family and selects arm64 in the control, allowing native inventory to report both slices. These admission paths are unchanged at the rechecked main revision. This is REA's admission logic, not an identified bundled dependency failure. ### Additional context Prepared with AI assistance and independently verified against real Apple tools. No earlier known-good version was established. PR #5359 explicitly notes the thin arm64_32 admission refusal as an existing limitation and tests that architecture inside FAT; its encryption-reporting change does not repair admission. Issue #5404 and PR #5501 concern universal containers with no host-compatible slice, whereas this report concerns the thin-file CPU check. Closed #3389 concerns exact arm64e.x1 FAT naming. Those have different restoration signals.
Author
Owner

@Lew1sWong commented on GitHub (Oct 21, 2026):

Reproduced on current main (7d8e4e433), macOS arm64, Apple clang: the thin arm64_32 object fails list-architectures, inspect-macho and inspect-signature with "unsupported Mach-O architecture", while the arm64 and FAT controls succeed. I'd like to work on a fix and will open a PR shortly.

<!-- gh-comment-id:6308889123 --> @Lew1sWong commented on GitHub (Oct 21, 2026): Reproduced on current main (7d8e4e433), macOS arm64, Apple clang: the thin `arm64_32` object fails `list-architectures`, `inspect-macho` and `inspect-signature` with "unsupported Mach-O architecture", while the arm64 and FAT controls succeed. I'd like to work on a fix and will open a PR shortly.
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#3461
No description provided.