[GH-ISSUE #5637] [Bug] trace-dylib-resolution treats absolute paths inside the analyzed bundle as outside-target #3415

Closed
opened 2026-10-31 00:40:08 +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/5637

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

trace-dylib-resolution classifies an existing dylib physically inside the selected app as outside-target when its ordinary LC_LOAD_DYLIB install name is absolute. An @rpath load with an absolute in-bundle LC_RPATH has the same result. Both return an undetermined edge, omit the dylib from images, and omit its transitive dependency edge. The executable-relative control traces the same chain successfully.

An independent verifier compiled and ran all three fixtures and the CLI comparisons. The submitter replayed the reproduction at the same baseline. Only these self-built fixture executables were executed for the dyld comparison; REA itself performs static analysis.

Reproduction

Run this Bash block from the built source checkout on macOS arm64 with Apple clang, otool, Node and jq. It creates three app fixtures with App -> libBridge -> libLeaf and isolates REA from the user profile. The absolute install-name and absolute-rpath cases fail; the relative case is the control.

set -euo pipefail
work_dir="$(mktemp -d)"
S="$work_dir"
mkdir -p "$S"/{home,cfg,data,cache,state,tmp}
S=$(cd "$S" && pwd -P)
rea() { 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 "$@"; }
cat > "$S/leaf.c" <<'C'
int leaf(void) { return 4; }
C
cat > "$S/bridge.c" <<'C'
extern int leaf(void);
int bridge(void) { return leaf()+5; }
C
cat > "$S/main.c" <<'C'
#include <stdio.h>
extern int bridge(void);
int main(void) { printf("result=%d\n", bridge()); return 0; }
C
for mode in literal rpath relative; do
 bundle="$S/$mode.app"
 frameworks="$bundle/Contents/Frameworks"
 mkdir -p "$frameworks" "$bundle/Contents/MacOS"
 cat > "$bundle/Contents/Info.plist" <<'XML'
<?xml version="1.0" encoding="UTF-8"?>
<plist version="1.0"><dict><key>CFBundleExecutable</key><string>App</string><key>CFBundleIdentifier</key><string>example.path-check</string><key>CFBundlePackageType</key><string>APPL</string></dict></plist>
XML
 clang -arch arm64 -dynamiclib "$S/leaf.c" -install_name @loader_path/libLeaf.dylib -o "$frameworks/libLeaf.dylib"
 case "$mode" in
 literal) install_name="$frameworks/libBridge.dylib"; rpath_arg=() ;;
 rpath) install_name=@rpath/libBridge.dylib; rpath_arg=("-Wl,-rpath,$frameworks") ;;
 relative) install_name=@executable_path/../Frameworks/libBridge.dylib; rpath_arg=() ;;
 esac
 clang -arch arm64 -dynamiclib "$S/bridge.c" -L"$frameworks" -lLeaf -install_name "$install_name" -o "$frameworks/libBridge.dylib"
 clang -arch arm64 "$S/main.c" -L"$frameworks" -lBridge "${rpath_arg[@]}" -o "$bundle/Contents/MacOS/App"
 otool -L "$bundle/Contents/MacOS/App" > "$S/$mode.otool.txt"
 DYLD_PRINT_LIBRARIES=1 "$bundle/Contents/MacOS/App" > "$S/$mode.dyld.out" 2> "$S/$mode.dyld.err"
 rea trace-dylib-resolution "$bundle" --json > "$S/$mode.json"
 jq '{coverage:.normalized_result.coverage,images:[.normalized_result.images[].path],edges:[.normalized_result.edges[]|{loader,install_name,resolution,candidates}],findings:.normalized_result.findings}' "$S/$mode.json" > "$S/$mode.summary.json"
 jq -c --arg mode "$mode" '{mode:$mode,coverage:.normalized_result.coverage.status,images:[.normalized_result.images[].path],bridge:[.normalized_result.edges[]|select(.install_name|endswith("libBridge.dylib"))|{command,resolution,outcomes:[.candidates[].outcome]}],leaf_edges:[.normalized_result.edges[]|select(.install_name=="@loader_path/libLeaf.dylib")|.resolution.status],findings:.normalized_result.findings}' "$S/$mode.json"
 cat "$S/$mode.dyld.out"
 sed -n '/libBridge.dylib/p; /libLeaf.dylib/p' "$S/$mode.dyld.err"
done
rea trace-dylib-resolution "$S/literal.app" --root Contents/Frameworks/libBridge.dylib --json > "$S/explicit-root.json"
jq -c '{images:[.normalized_result.images[].path],leaf_edges:[.normalized_result.edges[]|select(.install_name=="@loader_path/libLeaf.dylib")|.resolution.status]}' "$S/explicit-root.json"

Each verification run observed the failure once per affected variant. All three fixture executables printed result=9 and exited 0; DYLD_PRINT_LIBRARIES recorded both bundled libraries loading. All REA calls exited 0. The explicit --root call recovered the otherwise omitted libBridge -> libLeaf subtree.

Expected behavior

Resolve compatible libraries physically inside the selected app, include their image paths and digests, and trace their ordinary dependencies. Preserve outside-target/undetermined results for genuinely external paths without reading arbitrary host libraries.

The public tool contract at src/contracts/artifactToolContracts.ts:117-119 promises each-load tracing and limits the absolute/system-path exception to paths outside the analyzed root. docs/native-investigation.md:349-355 makes the same root distinction; docs/apple-application-analysis.md:206-211 distinguishes in-root resolutions from outside candidates. The Apple guide's absolute-path paragraph uses system libraries as its example without explicitly stating an in-root exception, so this expectation rests on the explicit root distinction in the contract and native guide.

Impact and scope

P2: valid app bundles using absolute paths to their own libraries lose images and transitive edges from dependency analysis. Both tested absolute-path forms reproduce; production frequency, other architectures and MCP transport were not tested. No launch failure, security-boundary crossing or data loss was demonstrated.

A checked manual workaround is to inspect the parent with otool and select the omitted library with --root to recover its subtree. That does not repair the parent edge or create one combined trace. Complete coverage accompanies the omission, but complete coverage alone does not promise that all dependencies resolve or that an app can launch.

REA version

rea-agents 6.7.0, source baseline 5b676ba9f302228b44c28ab076525abed3e647c1, built source CLI. Fetched and source-rechecked main at 17f581ff75d2b6cf643a997275e3e296f6c95760: none of the cited contract, documentation, path-expansion, resolver or reader files changed after the baseline. No runtime reproduction on that newer revision is claimed.

Runtime and platform

Node.js v26.12.0; macOS 27.0.1 arm64; Apple clang 21.0.0, clang-2100.3.34.2. No Hopper/Ghidra provider is involved.

Target and artifact context

Self-built arm64 Mach-O .app fixtures. App ordinarily loads bundled libBridge, which loads @loader_path/libLeaf.dylib. Variants differ in the parent's install name or rpath: absolute in-bundle install name, @rpath with absolute in-bundle rpath, and @executable_path/../Frameworks control. No symlinks or external dependency lookup are needed for the affected edges.

Interface

CLI

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

Observed CLI projections, with the private absolute prefix omitted:

Variant Images Bridge candidate / resolution Bridge -> Leaf edge
Absolute install name App only outside-target / undetermined absent
Absolute rpath App only outside-target / undetermined absent
Executable-relative control App, libBridge, libLeaf resolved / resolved resolved

All three results reported coverage.status: "complete" and findings: []. Both affected Bridge edges had command: "LC_LOAD_DYLIB", and their candidate paths retained the actual in-bundle location. Explicit --root Contents/Frameworks/libBridge.dylib returned a resolved Leaf edge. /usr/lib/libSystem.B.dylib remained outside-target/undetermined in all variants.

Evidence provenance: observed CLI, otool, and dyld output. Local verification: independently reproduced and replayed at the baseline. These successful fixture launches establish missing trace edges, not an absent-library launch failure.

Typed outcome or recovery expected

For both absolute variants, return a resolved App -> Bridge edge naming Contents/Frameworks/libBridge.dylib, include Bridge and Leaf in images, and return a resolved Bridge -> Leaf edge. Retain the relative control's resolved chain and the external system-path limitation.

Analysis or suspected cause

Source-derived reasoning: src/domain/apple/dyldPaths.ts:86-109 assigns every slash-prefixed path outside scope without comparing it with the analyzed root. src/domain/apple/dylibResolution.ts:246-307 uses this expansion for literal names and rpath bases, then returns outside-target before in-root lookup. Traversal at lines 528-541 receives no resolved image. The filesystem reader knows the real analyzed root at src/artifacts/apple/DylibResolutionReader.ts:109-136, while the expansion context carries only the owner directory and executable. This is application path-scope logic, not an identified bundled dependency defect.

Additional context

Prepared with AI assistance. Related #5551 and PR #5582 concern unparsed LC_LAZY_LOAD_DYLIB_INFO dependencies. This report uses ordinary dependencies that are parsed and returned, then incorrectly classified outside; parsing the lazy encoding would not restore this containment case. #5362 and PR #5363 concern line terminators in token suffixes. #4307 and closed PR #947 concern external shared-cache/system libraries, which remain outside this report's scope. Searches found no covering report for these in-bundle absolute paths.

Last known good version: NOT_ENOUGH_INFO; no earlier-version comparison was run. No implementation or successful restoration has been tested.

Originally created by @coygeek on GitHub (Oct 20, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/5637 ### 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 `trace-dylib-resolution` classifies an existing dylib physically inside the selected app as `outside-target` when its ordinary `LC_LOAD_DYLIB` install name is absolute. An `@rpath` load with an absolute in-bundle `LC_RPATH` has the same result. Both return an undetermined edge, omit the dylib from `images`, and omit its transitive dependency edge. The executable-relative control traces the same chain successfully. An independent verifier compiled and ran all three fixtures and the CLI comparisons. The submitter replayed the reproduction at the same baseline. Only these self-built fixture executables were executed for the dyld comparison; REA itself performs static analysis. ### Reproduction Run this Bash block from the built source checkout on macOS arm64 with Apple clang, `otool`, Node and `jq`. It creates three app fixtures with App -> libBridge -> libLeaf and isolates REA from the user profile. The absolute install-name and absolute-rpath cases fail; the relative case is the control. ```sh set -euo pipefail work_dir="$(mktemp -d)" S="$work_dir" mkdir -p "$S"/{home,cfg,data,cache,state,tmp} S=$(cd "$S" && pwd -P) rea() { 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 "$@"; } cat > "$S/leaf.c" <<'C' int leaf(void) { return 4; } C cat > "$S/bridge.c" <<'C' extern int leaf(void); int bridge(void) { return leaf()+5; } C cat > "$S/main.c" <<'C' #include <stdio.h> extern int bridge(void); int main(void) { printf("result=%d\n", bridge()); return 0; } C for mode in literal rpath relative; do bundle="$S/$mode.app" frameworks="$bundle/Contents/Frameworks" mkdir -p "$frameworks" "$bundle/Contents/MacOS" cat > "$bundle/Contents/Info.plist" <<'XML' <?xml version="1.0" encoding="UTF-8"?> <plist version="1.0"><dict><key>CFBundleExecutable</key><string>App</string><key>CFBundleIdentifier</key><string>example.path-check</string><key>CFBundlePackageType</key><string>APPL</string></dict></plist> XML clang -arch arm64 -dynamiclib "$S/leaf.c" -install_name @loader_path/libLeaf.dylib -o "$frameworks/libLeaf.dylib" case "$mode" in literal) install_name="$frameworks/libBridge.dylib"; rpath_arg=() ;; rpath) install_name=@rpath/libBridge.dylib; rpath_arg=("-Wl,-rpath,$frameworks") ;; relative) install_name=@executable_path/../Frameworks/libBridge.dylib; rpath_arg=() ;; esac clang -arch arm64 -dynamiclib "$S/bridge.c" -L"$frameworks" -lLeaf -install_name "$install_name" -o "$frameworks/libBridge.dylib" clang -arch arm64 "$S/main.c" -L"$frameworks" -lBridge "${rpath_arg[@]}" -o "$bundle/Contents/MacOS/App" otool -L "$bundle/Contents/MacOS/App" > "$S/$mode.otool.txt" DYLD_PRINT_LIBRARIES=1 "$bundle/Contents/MacOS/App" > "$S/$mode.dyld.out" 2> "$S/$mode.dyld.err" rea trace-dylib-resolution "$bundle" --json > "$S/$mode.json" jq '{coverage:.normalized_result.coverage,images:[.normalized_result.images[].path],edges:[.normalized_result.edges[]|{loader,install_name,resolution,candidates}],findings:.normalized_result.findings}' "$S/$mode.json" > "$S/$mode.summary.json" jq -c --arg mode "$mode" '{mode:$mode,coverage:.normalized_result.coverage.status,images:[.normalized_result.images[].path],bridge:[.normalized_result.edges[]|select(.install_name|endswith("libBridge.dylib"))|{command,resolution,outcomes:[.candidates[].outcome]}],leaf_edges:[.normalized_result.edges[]|select(.install_name=="@loader_path/libLeaf.dylib")|.resolution.status],findings:.normalized_result.findings}' "$S/$mode.json" cat "$S/$mode.dyld.out" sed -n '/libBridge.dylib/p; /libLeaf.dylib/p' "$S/$mode.dyld.err" done rea trace-dylib-resolution "$S/literal.app" --root Contents/Frameworks/libBridge.dylib --json > "$S/explicit-root.json" jq -c '{images:[.normalized_result.images[].path],leaf_edges:[.normalized_result.edges[]|select(.install_name=="@loader_path/libLeaf.dylib")|.resolution.status]}' "$S/explicit-root.json" ``` Each verification run observed the failure once per affected variant. All three fixture executables printed `result=9` and exited 0; `DYLD_PRINT_LIBRARIES` recorded both bundled libraries loading. All REA calls exited 0. The explicit `--root` call recovered the otherwise omitted libBridge -> libLeaf subtree. ### Expected behavior Resolve compatible libraries physically inside the selected app, include their image paths and digests, and trace their ordinary dependencies. Preserve outside-target/undetermined results for genuinely external paths without reading arbitrary host libraries. The public tool contract at `src/contracts/artifactToolContracts.ts:117-119` promises each-load tracing and limits the absolute/system-path exception to paths outside the analyzed root. `docs/native-investigation.md:349-355` makes the same root distinction; `docs/apple-application-analysis.md:206-211` distinguishes in-root resolutions from outside candidates. The Apple guide's absolute-path paragraph uses system libraries as its example without explicitly stating an in-root exception, so this expectation rests on the explicit root distinction in the contract and native guide. ### Impact and scope P2: valid app bundles using absolute paths to their own libraries lose images and transitive edges from dependency analysis. Both tested absolute-path forms reproduce; production frequency, other architectures and MCP transport were not tested. No launch failure, security-boundary crossing or data loss was demonstrated. A checked manual workaround is to inspect the parent with `otool` and select the omitted library with `--root` to recover its subtree. That does not repair the parent edge or create one combined trace. Complete coverage accompanies the omission, but complete coverage alone does not promise that all dependencies resolve or that an app can launch. ### REA version `rea-agents` 6.7.0, source baseline `5b676ba9f302228b44c28ab076525abed3e647c1`, built source CLI. Fetched and source-rechecked main at `17f581ff75d2b6cf643a997275e3e296f6c95760`: none of the cited contract, documentation, path-expansion, resolver or reader files changed after the baseline. No runtime reproduction on that newer revision is claimed. ### Runtime and platform Node.js v26.12.0; macOS 27.0.1 arm64; Apple clang 21.0.0, clang-2100.3.34.2. No Hopper/Ghidra provider is involved. ### Target and artifact context Self-built arm64 Mach-O `.app` fixtures. App ordinarily loads bundled libBridge, which loads `@loader_path/libLeaf.dylib`. Variants differ in the parent's install name or rpath: absolute in-bundle install name, `@rpath` with absolute in-bundle rpath, and `@executable_path/../Frameworks` control. No symlinks or external dependency lookup are needed for the affected edges. ### Interface CLI ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence Observed CLI projections, with the private absolute prefix omitted: | Variant | Images | Bridge candidate / resolution | Bridge -> Leaf edge | | --- | --- | --- | --- | | Absolute install name | App only | outside-target / undetermined | absent | | Absolute rpath | App only | outside-target / undetermined | absent | | Executable-relative control | App, libBridge, libLeaf | resolved / resolved | resolved | All three results reported `coverage.status: "complete"` and `findings: []`. Both affected Bridge edges had `command: "LC_LOAD_DYLIB"`, and their candidate paths retained the actual in-bundle location. Explicit `--root Contents/Frameworks/libBridge.dylib` returned a resolved Leaf edge. `/usr/lib/libSystem.B.dylib` remained outside-target/undetermined in all variants. Evidence provenance: observed CLI, `otool`, and dyld output. Local verification: independently reproduced and replayed at the baseline. These successful fixture launches establish missing trace edges, not an absent-library launch failure. ### Typed outcome or recovery expected For both absolute variants, return a resolved App -> Bridge edge naming `Contents/Frameworks/libBridge.dylib`, include Bridge and Leaf in `images`, and return a resolved Bridge -> Leaf edge. Retain the relative control's resolved chain and the external system-path limitation. ### Analysis or suspected cause Source-derived reasoning: `src/domain/apple/dyldPaths.ts:86-109` assigns every slash-prefixed path outside scope without comparing it with the analyzed root. `src/domain/apple/dylibResolution.ts:246-307` uses this expansion for literal names and rpath bases, then returns outside-target before in-root lookup. Traversal at lines 528-541 receives no resolved image. The filesystem reader knows the real analyzed root at `src/artifacts/apple/DylibResolutionReader.ts:109-136`, while the expansion context carries only the owner directory and executable. This is application path-scope logic, not an identified bundled dependency defect. ### Additional context Prepared with AI assistance. Related #5551 and PR #5582 concern unparsed `LC_LAZY_LOAD_DYLIB_INFO` dependencies. This report uses ordinary dependencies that are parsed and returned, then incorrectly classified outside; parsing the lazy encoding would not restore this containment case. #5362 and PR #5363 concern line terminators in token suffixes. #4307 and closed PR #947 concern external shared-cache/system libraries, which remain outside this report's scope. Searches found no covering report for these in-bundle absolute paths. Last known good version: NOT_ENOUGH_INFO; no earlier-version comparison was run. No implementation or successful restoration has been tested.
Author
Owner

@matbrgz commented on GitHub (Oct 20, 2026):

Cross-referencing with canonical issue #5362 ([Bug] Dylib path expansion rejects line terminators in dependency names and rpaths) and sibling #5551. Relates to bundle boundary detection, absolute path containment, and dependency tracing in trace-dylib-resolution. Cataloged under Cluster BC in consolidation tracker #4330.

<!-- gh-comment-id:6297499040 --> @matbrgz commented on GitHub (Oct 20, 2026): Cross-referencing with canonical issue #5362 (`[Bug] Dylib path expansion rejects line terminators in dependency names and rpaths`) and sibling #5551. Relates to bundle boundary detection, absolute path containment, and dependency tracing in `trace-dylib-resolution`. Cataloged under Cluster BC in consolidation tracker #4330.
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#3415
No description provided.