[GH-ISSUE #3384] [Bug] Mach-O dependency inspection removes install-name whitespace #1249

Closed
opened 2026-10-16 21:03:21 +00:00 by bsakaguchi · 0 comments
Owner

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

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 changes the valid dylib install name " leading.dylib " to "leading.dylib" and reports exhaustive dependency coverage. Its import result preserves the original spaced name, so one successful response contains conflicting library identities. Leading and trailing spaces belong to the recorded name and can change which file a loader resolves.

The CLI exits with status 0. The raw load-command name field also loses its leading spaces. Native otool output preserves the original spaced name. The consumer executable's SHA-256 was unchanged after inspection.

Reproduction

  1. Clone the repository, select the baseline, and prepare a clean runtime with the locked dependencies.
git clone https://github.com/morluto/rea.git rea
cd rea
git checkout 8dde6370d0809ad04cda3342d4e6f290f48aa063
npm ci
npm run build:cached
  1. From the repository root, create a dylib and consumer outside the checkout, then inspect them with the Apple tools and the public CLI. The quoted linker argument contains two spaces before and after leading.dylib.
rea_root="$PWD"
probe_root="$(mktemp -d -t rea-macho-dependency)"
cd "$probe_root"
mkdir home tmp
cat > library.c <<'C'
int exported_value(void) { return 42; }
C
cat > consumer.c <<'C'
extern int exported_value(void);
int main(void) { return exported_value(); }
C
xcrun clang -dynamiclib library.c \
  '-Wl,-install_name,  leading.dylib  ' -o whitespace.dylib
xcrun clang consumer.c ./whitespace.dylib -o consumer
xcrun otool -l consumer
env HOME="$PWD/home" TMPDIR="$PWD/tmp" \
  node "$rea_root/scripts/rea.mjs" inspect-macho "$PWD/consumer" --json
  1. Compare the recorded name in native otool output with the affected dependency path, import source, and raw load-command name in REA's successful result. The fixture binaries are inspected statically and need not be executed.

Expected behavior

The recorded LC_LOAD_DYLIB dependency path should remain " leading.dylib ". The dependency identity, corresponding import source, and raw load-command name should retain the original name rather than normalize its whitespace away.

docs/native-investigation.md:272 promises original raw load-command values. AGENTS.md:13,19,29 requires preservation of paths, identities, metadata, and source values. src/contracts/native/nativeToolContracts.ts:95-96 advertises dependency inspection.

Impact and scope

Impact level: P2, supported. A successful static inspection returns the wrong dependency identity for valid names with surrounding whitespace. This can mislead dependency matching and reconstruction, while the supplied artifact remains intact.

A workaround is available. trace-dylib-resolution on the same consumer returned edges[0].install_name: " leading.dylib ". Native otool also preserves the name. These provide the correct dependency fact but do not correct the existing inspect_macho Evidence.

REA version

6.4.0 (main at 8dde6370d0)

Runtime and platform

Node v26.10.0, macOS 27.0.1 arm64, Apple clang 21.0.0. The observed reproduction used the existing built runtime without source changes.

Target and artifact context

Source-built Mach-O dylib and consumer executable created from the inline C files above. The dependency has a valid install name with two leading and two trailing spaces. The recorded name, conflicting normalized identities, successful exit, and unchanged input were observed with these source-built files. The public CLI reproduction was complete and reproduced locally.

Interface

CLI

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

The successful normalized result contains the following fields. This excerpt selects the affected dependency and import and omits unrelated fields.

CLI exit status: 0
{
  "dependencies": {
    "items": [{ "path": "leading.dylib", "kind": "LC_LOAD_DYLIB" }],
    "exhaustive": true,
    "limitations": []
  },
  "imports": {
    "items": [{ "name": "_exported_value", "source": "  leading.dylib  " }]
  }
}

Typed outcome or recovery expected

Inspect the same consumer and require the affected dependency path and import source to equal " leading.dylib " exactly. Verify that the raw load-command value preserves its name payload, and keep an ordinary install-name control unchanged. The passing result must preserve the input bytes and avoid conflating the spaced name with "leading.dylib".

Analysis or suspected cause

Source-derived reasoning: src/native/parsers/otool.ts:440-446 consumes or trims leading name whitespace in parseField, and src/native/parsers/otool.ts:584-585 removes trailing name whitespace with the displayed offset suffix in stripOffsetSuffix. These transformations explain the altered dependency name and raw load-command name; the public CLI reproduction establishes the observed failure.

Additional context

  • Related #765, closed as completed, concerns CFBundleExecutable whitespace lost during plist decoding before target resolution; this report concerns a dependency install name altered after successful target admission.
  • Related #1990, closed as completed, concerns missing scalar values accepted from truncated otool output; this report concerns a valid nonempty name whose whitespace is removed.
  • Related #1970, closed as completed, concerns rejected dylib-use commands with no legacy timestamp; this report concerns successful inspection with a changed install name.
  • Related #2661, closed as completed, concerns conflated architecture slices in dylib-environment findings; this report concerns whitespace loss in the otool dependency parser.
  • Related #760, closed as completed, concerns line breaks in target filenames corrupting architecture inventory; this report concerns the separate load-command name parser.
  • Related #3176, open, concerns a JVM class resource misclassified during dylib-root discovery; this report concerns an admitted Mach-O dependency name.
Originally created by @coygeek on GitHub (Oct 14, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/3384 ### 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` changes the valid dylib install name `" leading.dylib "` to `"leading.dylib"` and reports exhaustive dependency coverage. Its import result preserves the original spaced name, so one successful response contains conflicting library identities. Leading and trailing spaces belong to the recorded name and can change which file a loader resolves. The CLI exits with status 0. The raw load-command `name` field also loses its leading spaces. Native `otool` output preserves the original spaced name. The consumer executable's SHA-256 was unchanged after inspection. ### Reproduction 1. Clone the repository, select the baseline, and prepare a clean runtime with the locked dependencies. ```sh git clone https://github.com/morluto/rea.git rea cd rea git checkout 8dde6370d0809ad04cda3342d4e6f290f48aa063 npm ci npm run build:cached ``` 2. From the repository root, create a dylib and consumer outside the checkout, then inspect them with the Apple tools and the public CLI. The quoted linker argument contains two spaces before and after `leading.dylib`. ```sh rea_root="$PWD" probe_root="$(mktemp -d -t rea-macho-dependency)" cd "$probe_root" mkdir home tmp cat > library.c <<'C' int exported_value(void) { return 42; } C cat > consumer.c <<'C' extern int exported_value(void); int main(void) { return exported_value(); } C xcrun clang -dynamiclib library.c \ '-Wl,-install_name, leading.dylib ' -o whitespace.dylib xcrun clang consumer.c ./whitespace.dylib -o consumer xcrun otool -l consumer env HOME="$PWD/home" TMPDIR="$PWD/tmp" \ node "$rea_root/scripts/rea.mjs" inspect-macho "$PWD/consumer" --json ``` 3. Compare the recorded name in native `otool` output with the affected dependency path, import source, and raw load-command name in REA's successful result. The fixture binaries are inspected statically and need not be executed. ### Expected behavior The recorded `LC_LOAD_DYLIB` dependency path should remain `" leading.dylib "`. The dependency identity, corresponding import source, and raw load-command name should retain the original name rather than normalize its whitespace away. `docs/native-investigation.md:272` promises original raw load-command values. `AGENTS.md:13,19,29` requires preservation of paths, identities, metadata, and source values. `src/contracts/native/nativeToolContracts.ts:95-96` advertises dependency inspection. ### Impact and scope Impact level: P2, supported. A successful static inspection returns the wrong dependency identity for valid names with surrounding whitespace. This can mislead dependency matching and reconstruction, while the supplied artifact remains intact. A workaround is available. `trace-dylib-resolution` on the same consumer returned `edges[0].install_name: " leading.dylib "`. Native `otool` also preserves the name. These provide the correct dependency fact but do not correct the existing `inspect_macho` Evidence. ### REA version 6.4.0 (main at 8dde6370d0809ad04cda3342d4e6f290f48aa063) ### Runtime and platform Node v26.10.0, macOS 27.0.1 arm64, Apple clang 21.0.0. The observed reproduction used the existing built runtime without source changes. ### Target and artifact context Source-built Mach-O dylib and consumer executable created from the inline C files above. The dependency has a valid install name with two leading and two trailing spaces. The recorded name, conflicting normalized identities, successful exit, and unchanged input were observed with these source-built files. The public CLI reproduction was complete and reproduced locally. ### Interface CLI ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence The successful normalized result contains the following fields. This excerpt selects the affected dependency and import and omits unrelated fields. ```text CLI exit status: 0 { "dependencies": { "items": [{ "path": "leading.dylib", "kind": "LC_LOAD_DYLIB" }], "exhaustive": true, "limitations": [] }, "imports": { "items": [{ "name": "_exported_value", "source": " leading.dylib " }] } } ``` ### Typed outcome or recovery expected Inspect the same consumer and require the affected dependency path and import source to equal `" leading.dylib "` exactly. Verify that the raw load-command value preserves its name payload, and keep an ordinary install-name control unchanged. The passing result must preserve the input bytes and avoid conflating the spaced name with `"leading.dylib"`. ### Analysis or suspected cause Source-derived reasoning: `src/native/parsers/otool.ts:440-446` consumes or trims leading name whitespace in `parseField`, and `src/native/parsers/otool.ts:584-585` removes trailing name whitespace with the displayed offset suffix in `stripOffsetSuffix`. These transformations explain the altered dependency name and raw load-command name; the public CLI reproduction establishes the observed failure. ### Additional context - Related [#765](https://github.com/morluto/rea/issues/765), closed as completed, concerns CFBundleExecutable whitespace lost during plist decoding before target resolution; this report concerns a dependency install name altered after successful target admission. - Related [#1990](https://github.com/morluto/rea/issues/1990), closed as completed, concerns missing scalar values accepted from truncated `otool` output; this report concerns a valid nonempty name whose whitespace is removed. - Related [#1970](https://github.com/morluto/rea/issues/1970), closed as completed, concerns rejected dylib-use commands with no legacy timestamp; this report concerns successful inspection with a changed install name. - Related [#2661](https://github.com/morluto/rea/issues/2661), closed as completed, concerns conflated architecture slices in dylib-environment findings; this report concerns whitespace loss in the `otool` dependency parser. - Related [#760](https://github.com/morluto/rea/issues/760), closed as completed, concerns line breaks in target filenames corrupting architecture inventory; this report concerns the separate load-command name parser. - Related [#3176](https://github.com/morluto/rea/issues/3176), open, concerns a JVM class resource misclassified during dylib-root discovery; this report concerns an admitted Mach-O dependency name.
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#1249
No description provided.