Commit Graph
9 Commits
Author SHA1 Message Date
morluto f83d97fe92 fix(native): classify observed dSYM inspection refusal 2026-10-24 04:05:23 +08:00
23196daa11 fix(apple): read LC_LAZY_LOAD_DYLIB_INFO dependency payloads (#5650)
* fix(apple): read LC_LAZY_LOAD_DYLIB_INFO dependency payloads

Parse the linkedit install name for lazy-load info commands, resolve those
edges without traversing them, and stop inspect-macho from treating an otool
dependency list as exhaustive when that payload is omitted.

Fixes #5551
Fixes #5649

Co-authored-by: morluto <morluto@users.noreply.github.com>

* fix(apple): bound lazy-load payload reads and image offsets

Reuse identical LC_LAZY_LOAD_DYLIB_INFO ranges, cap their combined size,
and reject flag or chain-start offsets outside the slice VM range.

Co-authored-by: morluto <morluto@users.noreply.github.com>

* fix(apple): bound lazy-load string scans and contained reads

Reject payload strings that start in the 24-byte header, validate each
file-offset string once, and reuse payload bytes already read for a
containing range.

Co-authored-by: morluto <morluto@users.noreply.github.com>

* fix(apple): reject cached lazy-load strings outside a payload

Remember each cached string's terminating offset and require that NUL to
fall inside the payload being decoded.

Co-authored-by: morluto <morluto@users.noreply.github.com>

* fix(apple): bound lazy-load payload range lookups

Count containment checks across earlier payload reads and stop at the
existing 16 MiB work limit.

Co-authored-by: morluto <morluto@users.noreply.github.com>

* fix(apple): count every lazy-info string reference

Charge repeated and overlapping LC_LAZY_LOAD_DYLIB_INFO scans against the
16 MiB validation budget, and stop a scan once that budget is exhausted.

Co-authored-by: morluto <morluto@users.noreply.github.com>

* fix(apple): cap retained lazy-info dependency results

Stop after 1024 LC_LAZY_LOAD_DYLIB_INFO commands so a load-command region
full of identical commands cannot become a million traced edges.

Co-authored-by: morluto <morluto@users.noreply.github.com>

* fix(apple): reject a lazy-load path NUL in the final byte

dyld stops the load-path scan before the payload's last byte, so a
terminator in that byte leaves the image malformed.

Co-authored-by: morluto <morluto@users.noreply.github.com>

* fix(apple): keep may-be-missing lazy loads optional

A missing LC_LAZY_LOAD_DYLIB_INFO with the may-be-missing bit is weak, so the lazy finding says dyld continues on first use instead of failing.

Co-authored-by: morluto <morluto@users.noreply.github.com>

* fix(apple): cap lazy-info symbol string references

A payload of distinct one-byte symbol strings stayed under the byte budgets while caching one entry per offset. A slice now stops at 65536 symbol references.

Co-authored-by: morluto <morluto@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-21 03:33:07 +08:00
morluto cf3288d8cf fix(native): preserve Mach-O container framing across inspection 2026-10-20 17:16:44 +08:00
Kaoru0822andClaude Opus 5.5 3f5b703379 fix(native): derive Mach-O word size and byte order from the selected slice (#3163)
* fix(native): derive Mach-O word size and byte order from the selected slice

inspect_macho read endian and word_size from `file -b` for the whole
file. macOS `file` never prints a Mach-O byte order, so endian was null
for every target. For a universal binary, word_size matched "64-bit"
anywhere in the summary instead of describing the selected slice, and it
was null for a thin 32-bit slice.

Both now come from the selected slice's otool header: the magic gives
the word size and the CPU family gives the byte order (x86 and ARM
little, PowerPC big, other families unknown). The file capture is no
longer needed by inspect_macho, so it is no longer run there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(native): select a 32-bit slice beside a 64-bit slice through inspect_macho

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 22:44:41 -07:00
morluto c02fb0ea80 fix(native): rank Mach-O slices by parsed CPU subtype 2026-10-13 16:43:12 +08:00
96fc84f1fb fix(native): select one slice when universal Mach-O slices share a CPU type (#2040)
* fix(native): select one slice when universal Mach-O slices share a CPU type

On macOS 27 most system binaries are universal files whose slices share
CPU_TYPE_ARM64: arm64e + arm64e.x1 (Calculator, TextEdit), x86_64 + arm64e +
arm64e.x1 (/bin/ls), or arm64 + arm64e.x1 (ShortcutDroplet). Target admission
names slices by CPU family, so these targets have availableArchitectures
["arm64"] while the file holds two arm64 slices.

inspect_macho read that family count as a slice count and ran otool, nm,
dyld_info, dwarfdump and vtool without -arch. ShortcutDroplet failed with
"Malformed otool load-command output" (40 load commands against ncmds 20).
Calculator reported both slices' imports (4138 instead of 2069), nm's
"(for architecture ...)" header lines as exports, and no selected slice.
selectArchitecture now also filters when lipo lists more than one slice. Its
preference is unchanged: the family name, then arm64e, then the first
arm64e.<variant>.

inspect_native_dispatch_metadata matched FAT slices by CPU type only and
refused /bin/ls and TextEdit as "Ambiguous FAT architecture slice".
selectMachoSlice now ranks slices of the requested CPU type by base subtype in
the same order: arm64 (0, then 1) or x86_64 (3), then arm64e (2), then other
variants such as arm64e.x1 in FAT table order. It reports ambiguity only when
the top-ranked slices share a subtype.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(native): share Mach-O slice preference across inspections

* chore: preserve upstream process capture test during merge

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 16:36:23 +08:00
Kaoru0822andClaude Opus 5.5 2c7240cf9d perf(native): run independent Mach-O slice commands concurrently (#2466)
inspect_macho launched otool, nm, two dyld_info reads, dwarfdump and
vtool one at a time. Each launch also resolves the tool through
`xcrun --find`, so a warm call spent about 350 ms in eight serial
supervised launches. The slice commands only read the selected
artifact, so launch them together. Every launch still settles before
the result, and declared order still fixes provenance and which
failure is reported.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 21:12:05 -07:00
morluto 6fb3a345fe fix(analysis): preserve producer identity and partial evidence
Reconcile Mach-O UUID observations without hiding malformed or conflicting sources. Require source-map identity digests to be supported by consistent observations. Keep truncated Android manifests as partial raw evidence, validate WABT section framing, and reject missing JEB inventories or error-marked success replies.

Consolidate native UUID parsing and simplify capacity and malformed-input fixtures. Focused source and public workflow checks pass, including native arm64e inspection and real WABT framing probes; a successful real JEB GUI project opening remains unverified.
2026-10-12 12:37:00 +08:00
morlutoandCursor Agent b4db35f092 fix: bind identity and coverage to the producing rule (#1513)
* fix: bind identity and coverage to the producing rule

Logical paths, container families, examined bytes, path-segment conventions, destination collisions, file-id leases, Mach-O CPU selection, and firmware cleanup now follow the observation that owns each rule.

* fix: fail unmatched Mach-O slices and keep firmware roots independent

A fat target whose architecture lipo did not list now stops before slice tools run. Chained fixups still count unread LC_DYLD_INFO rebase bytes, and a firmware cleanup failure no longer blocks the next workspace.

* test: cover path identity and runtime families at the projection boundary

NFC joins and segment conventions are now asserted by the path resolver, the Apple projection, and a real APK inventory instead of helper unit tests.

* test: expect unread dyld rebase bytes in the Apple dispatch fixture

The legacy LC_DYLD_INFO fixture keeps a rebase range this reader does not decode, so the public CLI and MCP check now requires that partial coverage beside the bind opcodes.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-10-10 17:14:47 +08:00