* refactor(macho): extract non-throwing container and slice load-command readers
Split readMachoImage into readMachoContainer, which classifies a file as
thin, FAT or not Mach-O and keeps FAT range issues per slice, and
readSliceLoadCommands, which reads one slice header and frames its load
commands, returning every command framed before a failure.
readMachoImage keeps its strict policy on top of them: commands framed
before a framing defect still decode first, an unsupported FAT slice
still yields to a later malformed one, and the first FAT range issue is
still reported. A characterization test pins that precedence for inputs
with several defects; it passes against both the previous and the new
implementation.
Part of #4306.
* feat(macho): report per-slice encryption status in inspect_macho
inspect_macho now reports the LC_ENCRYPTION_INFO(_64) state of every
slice in a new `encryption` collection, read in process from load
commands without reading code bytes.
- Each slice is encrypted, not-encrypted, absent, malformed or unknown.
Absent requires a completed load-command walk; malformed and unknown
carry a reason and keep commands read before the failure.
- The named range is end-exclusive in slice and input-file coordinates
and maps to file-backed segment fragments, VM addresses and the
overlapping nonempty sections.
- A ciphertext range adds a limitation naming its input-file bytes.
- The in-process read and the native tools are bracketed by one regular
file descriptor and stat identity checks; a change reports
artifact_changed. A missing or inaccessible target keeps the tool
facts and reports encryption as non-exhaustive, never absent.
- Retained Evidence recorded before this field is parsed by the owning
schema and reports encryption as unknown in the macho view and in
managed/native verification.
Fixes#4306
* fix(macho): keep slice facts and failures under large tables and close errors
- Frame load commands through a visitor instead of retaining every
command, restoring the pre-refactor memory profile (2M eight-byte
commands: peak heap 370 MiB -> 0.9 MiB).
- Confirm a large FAT container from a valid slice's Mach-O header so a
sibling's range fault stays per slice instead of hiding healthy slices.
- Name only sections whose bytes the range and their segment both cover.
- Throw inspection failures inside the owned descriptor callback so a
close failure keeps them as its primary error.
* fix(macho): map encrypted ranges only within segment VM extents
mapRange gave every file-backed segment fragment VM addresses, even for a
segment with no VM size, whose file bytes dyld never maps, and past vmsize
when a segment declared more file bytes than memory.
Read vmsize with the segment. A segment with no VM size keeps its fragment
with null VM addresses. A file size beyond the VM size, or a VM range past
the address space, makes the mapping unknown with a reason while the slice
keeps its encryption status.
The docs also claimed the encryption read never reads code bytes. Like
every Mach-O container read, a 0xcafebabe input of at most 16 MiB is read
whole to rule out a JVM class file; say so.
* fix(macho): read encryption through the admitted descriptor facts
Main's withArtifactRegularFile now hands callers the admitted handle and
its initial stats. Use those facts instead of re-stating the handle, and
pass the legacy Evidence payload straight to createEvidence.
* fix(macho): retain a finished report when the target close fails
The file owner keeps the completed callback value when the descriptor close
fails or cancellation arrives during close. inspect_macho now validates that
value and retains the report as the error's partial observation, as
inspect_signature does, instead of returning only the cleanup or
cancellation error.
* test(native): supply readEncryption in merged dyld refusal cases
main's new dyld refusal cases build NativeMachoInspectionContext without
the encryption reader this branch requires.
* test(views): bind the Mach-O encryption page to its facet
main's facet-ownership case builds inspect_macho Evidence without the
encryption coverage this branch requires; the fixture now carries one
slice row and the loop checks the encryption page like the other facets.
* fix(macho): bound encryption commands and range mapping across slices
A slice retains at most two encryption load commands, since a second one
already makes it malformed; the reason keeps the full count. Range mapping
in every slice shares one budget of segment and section records, charged
from each segment command's capacity before it is decoded. A slice past
the budget keeps its encryption status and reports an unknown mapping.
* style(macho): format encryption reader and binding test
* fix(macho): stop retaining segments past the mapping budget
Segment commands are collected during framing only while their mapping
records still fit in the budget shared by all slices, and the budget is
charged only when the slice maps its range.
* docs(macho): clarify retained mapping allowance
* fix(ci): own the Mach-O encryption test in Apple scope
---------
Co-authored-by: t <t@t>
Co-authored-by: gensokyo-dev <[email protected]>