[lldb][Mach-O] Fix load-command loops spinning on cmdsize = 0 (#205134)
Every function in `ObjectFileMachO` and `ObjectContainerMachOFileset`
that iterates over load commands advances the file offset by
`lc.cmdsize` after reading each command. A malformed command with
cmdsize smaller than `sizeof(load_command)` (in particular cmdsize = 0)
does not make forward progress, so the loop spins for ncmds iterations.
With `ncmds` close to `INT_MAX` the function never returns in practice.
Factor the read-and-validate step into a static template helper
`ReadMachOCommand<T>` in each plugin's translation unit. It reads the
8-byte cmd/cmdsize header and returns false on EOF or on a cmdsize that
is too small to make forward progress. All load-command loops now use
this helper, replacing the previously duplicated GetU32 + cmdsize
check. `T` may be `llvm::MachO::load_command` or any of its richer
variants (uuid_command, dylib_command, thread_command, ident_command,
encryption_info_command, ...). The helper only touches the leading
cmd/cmdsize fields, leaving the rest of `T` for the caller to fill in.
Affected loops in `ObjectFileMachO`:
IsStripped, GetEncryptedFileRanges, CreateSections, ParseSymtab,
GetUUID (static), GetAllArchSpecs (two loops), GetDependentModules,
GetEntryPointAddress, GetNumThreadContexts, FindLC_NOTEByName,
GetIdentifierString, GetVersion, FindMinimumVersionInfo
And in ObjectContainerMachOFileset:
ParseFileset
Add unit tests (`ObjectFileMachOTest::ZeroCmdSize` and
`ObjectContainerMachOFilesetTest::ZeroCmdSize`) that feed a 40-byte
Mach-O with `ncmds = 0x7FFFFFFF` and `cmdsize = 0` into the relevant
parsers. Without the fix the tests spin ~2 billion iterations; with
the fix they return immediately. Found by lldb-target-fuzzer.
Assisted-by: Claude
GitOrigin-RevId: 20a2329148626aa66db555558af9d607035a8bf2
5 files changed