Skip to content

Commit 00340bf

Browse files
committed
Add bounds validation to Mach-O parser.
- Validate nfat_arch count in universal binary headers - Validate nsects count before iterating segment sections - Validate nsyms count in symbol table parsing - Add upper bound check for load command sizes
1 parent bec2cda commit 00340bf

File tree

1 file changed

+38
-5
lines changed

1 file changed

+38
-5
lines changed

src/macho.cc

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,16 @@ void ParseMachOHeaderImpl(string_view macho_data, RangeSink* overhead_sink,
9595
for (uint32_t i = 0; i < ncmds; i++) {
9696
auto command = GetStructPointer<load_command>(header_data);
9797

98-
// We test for this because otherwise a large ncmds can make bloaty hang for
99-
// a while, even on a small file. Hopefully there are no real cases where a
100-
// zero-size loadcmd exists.
98+
// Validate load command size to prevent hangs or buffer overruns
10199
if (command->cmdsize == 0) {
102100
THROW("Mach-O load command had zero size.");
103101
}
102+
if (command->cmdsize < sizeof(load_command)) {
103+
THROW("Mach-O load command size smaller than minimum.");
104+
}
105+
if (command->cmdsize > header_data.size()) {
106+
THROW("Mach-O load command size exceeds remaining header data.");
107+
}
104108

105109
LoadCommand data;
106110
data.is64bit = Is64Bit<Struct>();
@@ -163,6 +167,12 @@ void ParseFatHeader(string_view fat_file, RangeSink* overhead_sink,
163167
fat_file.substr(0, sizeof(fat_header)));
164168
assert(ByteSwap(header->magic) == FAT_MAGIC);
165169
uint32_t nfat_arch = ByteSwap(header->nfat_arch);
170+
171+
// Validate that nfat_arch count doesn't exceed header size
172+
if (nfat_arch > header_data.size() / sizeof(fat_arch)) {
173+
THROW("invalid nfat_arch count in universal binary header");
174+
}
175+
166176
for (uint32_t i = 0; i < nfat_arch; i++) {
167177
auto arch = GetStructPointerAndAdvance<fat_arch>(&header_data);
168178
string_view macho_data = StrictSubstr(
@@ -203,6 +213,12 @@ void AddSegmentAsFallback(string_view command_data, string_view file_data,
203213
string_view segname = ArrayToStr(segment->segname, 16);
204214

205215
uint32_t nsects = segment->nsects;
216+
217+
// Validate that nsects count doesn't exceed command data size
218+
if (nsects > command_data.size() / sizeof(Section)) {
219+
THROW("invalid section count in segment");
220+
}
221+
206222
for (uint32_t j = 0; j < nsects; j++) {
207223
auto section = GetStructPointerAndAdvance<Section>(&command_data);
208224

@@ -257,6 +273,12 @@ void ParseSegment(LoadCommand cmd, RangeSink* sink) {
257273
}
258274
} else if (sink->data_source() == DataSource::kSections) {
259275
uint32_t nsects = segment->nsects;
276+
277+
// Validate that nsects count doesn't exceed command data size
278+
if (nsects > cmd.command_data.size() / sizeof(Section)) {
279+
THROW("invalid section count in segment");
280+
}
281+
260282
for (uint32_t j = 0; j < nsects; j++) {
261283
auto section = GetStructPointerAndAdvance<Section>(&cmd.command_data);
262284

@@ -425,12 +447,17 @@ void ParseSymbolsFromSymbolTable(const LoadCommand& cmd, SymbolTable* table,
425447
RangeSink* sink) {
426448
auto symtab_cmd = GetStructPointer<symtab_command>(cmd.command_data);
427449

450+
// Validate that nsyms count doesn't cause overflow or exceed file size
451+
uint32_t nsyms = symtab_cmd->nsyms;
452+
if (nsyms > cmd.file_data.size() / sizeof(NList)) {
453+
THROW("invalid symbol count in symbol table");
454+
}
455+
428456
string_view symtab = StrictSubstr(cmd.file_data, symtab_cmd->symoff,
429-
symtab_cmd->nsyms * sizeof(NList));
457+
nsyms * sizeof(NList));
430458
string_view strtab =
431459
StrictSubstr(cmd.file_data, symtab_cmd->stroff, symtab_cmd->strsize);
432460

433-
uint32_t nsyms = symtab_cmd->nsyms;
434461
for (uint32_t i = 0; i < nsyms; i++) {
435462
auto sym = GetStructPointerAndAdvance<NList>(&symtab);
436463
string_view sym_range(reinterpret_cast<const char*>(sym), sizeof(NList));
@@ -507,6 +534,12 @@ void ReadDebugSectionsFromSegment(LoadCommand cmd, dwarf::File *dwarf,
507534
}
508535

509536
uint32_t nsects = segment->nsects;
537+
538+
// Validate that nsects count doesn't exceed command data size
539+
if (nsects > cmd.command_data.size() / sizeof(Section)) {
540+
THROW("invalid section count in segment");
541+
}
542+
510543
for (uint32_t j = 0; j < nsects; j++) {
511544
auto section = GetStructPointerAndAdvance<Section>(&cmd.command_data);
512545
string_view sectname = ArrayToStr(section->sectname, 16);

0 commit comments

Comments
 (0)