Index: llvm/trunk/lib/Object/MachOObjectFile.cpp =================================================================== --- llvm/trunk/lib/Object/MachOObjectFile.cpp +++ llvm/trunk/lib/Object/MachOObjectFile.cpp @@ -38,8 +38,12 @@ }; } -template +template static T getStruct(const MachOObjectFile *O, const char *P) { + // Don't read before the beginning or past the end of the file + if (P < O->getData().begin() || P + sizeof(T) > O->getData().end()) + report_fatal_error("Malformed MachO file."); + T Cmd; memcpy(&Cmd, P, sizeof(T)); if (O->isLittleEndian() != sys::IsLittleEndianHost) @@ -47,15 +51,26 @@ return Cmd; } +template +static uint32_t getSegmentLoadCommandNumSections(const SegmentCmd &S, + uint32_t Cmdsize) { + const unsigned SectionSize = sizeof(SegmentCmd); + if (S.nsects > std::numeric_limits::max() / SectionSize || + S.nsects * SectionSize > Cmdsize - sizeof(S)) + report_fatal_error( + "Number of sections too large for size of load command."); + return S.nsects; +} + static uint32_t getSegmentLoadCommandNumSections(const MachOObjectFile *O, const MachOObjectFile::LoadCommandInfo &L) { - if (O->is64Bit()) { - MachO::segment_command_64 S = O->getSegment64LoadCommand(L); - return S.nsects; - } - MachO::segment_command S = O->getSegmentLoadCommand(L); - return S.nsects; + if (O->is64Bit()) + return getSegmentLoadCommandNumSections(O->getSegment64LoadCommand(L), + L.C.cmdsize); + + return getSegmentLoadCommandNumSections(O->getSegmentLoadCommand(L), + L.C.cmdsize); } static bool isPageZeroSegment(const MachOObjectFile *O, @@ -281,6 +296,12 @@ } UuidLoadCmd = Load.Ptr; } else if (Load.C.cmd == SegmentLoadType) { + const unsigned SegmentLoadSize = this->is64Bit() + ? sizeof(MachO::segment_command_64) + : sizeof(MachO::segment_command); + if (Load.C.cmdsize < SegmentLoadSize) + report_fatal_error("Segment load command size is too small."); + uint32_t NumSections = getSegmentLoadCommandNumSections(this, Load); for (unsigned J = 0; J < NumSections; ++J) { const char *Sec = getSectionPtr(this, Load, J); @@ -315,6 +336,8 @@ StringRef StringTable = getStringTableData(); MachO::nlist_base Entry = getSymbolTableEntryBase(this, Symb); const char *Start = &StringTable.data()[Entry.n_strx]; + if (Start >= getData().end()) + report_fatal_error("Symbol name entry points past end of file."); Res = StringRef(Start); return object_error::success; } @@ -1204,7 +1227,8 @@ return basic_symbol_iterator(SymbolRef(DRI, this)); MachO::symtab_command Symtab = getSymtabLoadCommand(); - assert(Index < Symtab.nsyms && "Requested symbol index is out of range."); + if (Index >= Symtab.nsyms) + report_fatal_error("Requested symbol index is out of range."); unsigned SymbolTableEntrySize = is64Bit() ? sizeof(MachO::nlist_64) : sizeof(MachO::nlist); DRI.p = reinterpret_cast(getPtr(this, Symtab.symoff)); @@ -2108,6 +2132,8 @@ ArrayRef MachOObjectFile::getSectionRawName(DataRefImpl Sec) const { + if (Sec.d.a >= Sections.size()) + report_fatal_error("getSectionRawName: Invalid section index"); const section_base *Base = reinterpret_cast(Sections[Sec.d.a]); return makeArrayRef(Base->sectname); @@ -2115,6 +2141,8 @@ ArrayRef MachOObjectFile::getSectionRawFinalSegmentName(DataRefImpl Sec) const { + if (Sec.d.a >= Sections.size()) + report_fatal_error("getSectionRawFinalSegmentName: Invalid section index"); const section_base *Base = reinterpret_cast(Sections[Sec.d.a]); return makeArrayRef(Base->segname); @@ -2205,6 +2233,8 @@ sizeof(MachO::mach_header); Load.Ptr = getPtr(this, HeaderSize); Load.C = getStruct(this, Load.Ptr); + if (Load.C.cmdsize < 8) + report_fatal_error("Load command with size < 8 bytes."); return Load; } @@ -2213,14 +2243,22 @@ MachOObjectFile::LoadCommandInfo Next; Next.Ptr = L.Ptr + L.C.cmdsize; Next.C = getStruct(this, Next.Ptr); + if (Next.C.cmdsize < 8) + report_fatal_error("Load command with size < 8 bytes."); return Next; } MachO::section MachOObjectFile::getSection(DataRefImpl DRI) const { + // TODO: What if Sections.size() == 0? + if (DRI.d.a >= Sections.size()) + report_fatal_error("getSection: Invalid section index."); return getStruct(this, Sections[DRI.d.a]); } MachO::section_64 MachOObjectFile::getSection64(DataRefImpl DRI) const { + // TODO: What if Sections.size() == 0? + if (DRI.d.a >= Sections.size()) + report_fatal_error("getSection64: Invalid section index."); return getStruct(this, Sections[DRI.d.a]); } Index: llvm/trunk/test/Object/macho-invalid.test =================================================================== --- llvm/trunk/test/Object/macho-invalid.test +++ llvm/trunk/test/Object/macho-invalid.test @@ -0,0 +1,51 @@ +// No crash, might not be totally invalid +RUN: llvm-objdump -private-headers %p/Inputs/macho-invalid-zero-ncmds + +RUN: not llvm-objdump -private-headers %p/Inputs/macho64-invalid-incomplete-load-command 2>&1 \ +RUN: | FileCheck -check-prefix INCOMPLETE-LOADC %s + +RUN: not llvm-objdump -private-headers %p/Inputs/macho-invalid-too-small-load-command 2>&1 \ +RUN: | FileCheck -check-prefix SMALL-LOADC-SIZE %s +RUN: not llvm-objdump -private-headers %p/Inputs/macho64-invalid-too-small-load-command 2>&1 \ +RUN: | FileCheck -check-prefix SMALL-LOADC-SIZE %s + +RUN: not llvm-objdump -private-headers %p/Inputs/macho-invalid-too-small-segment-load-command 2>&1 \ +RUN: | FileCheck -check-prefix SMALL-SEGLOADC-SIZE %s +RUN: not llvm-objdump -private-headers %p/Inputs/macho64-invalid-too-small-segment-load-command 2>&1 \ +RUN: | FileCheck -check-prefix SMALL-SEGLOADC-SIZE %s + +RUN: not llvm-objdump -private-headers %p/Inputs/macho-invalid-no-size-for-sections 2>&1 \ +RUN: | FileCheck -check-prefix TOO-MANY-SECTS %s +RUN: not llvm-objdump -private-headers %p/Inputs/macho64-invalid-no-size-for-sections 2>&1 \ +RUN: | FileCheck -check-prefix TOO-MANY-SECTS %s + +RUN: not llvm-objdump -t %p/Inputs/macho-invalid-bad-symbol-index 2>&1 \ +RUN: | FileCheck -check-prefix BAD-SYMBOL %s + +RUN: not llvm-objdump -t %p/Inputs/macho-invalid-symbol-name-past-eof 2>&1 \ +RUN: | FileCheck -check-prefix NAME-PAST-EOF %s + +RUN: not llvm-objdump -t %p/Inputs/macho-invalid-section-index-getSectionRawFinalSegmentName 2>&1 \ +RUN: | FileCheck -check-prefix INVALID-SECTION-IDX-SEG-NAME %s + +RUN: not llvm-nm %p/Inputs/macho-invalid-section-index-getSectionRawName 2>&1 \ +RUN: | FileCheck -check-prefix INVALID-SECTION-IDX-SECT-NAME %s + +RUN: not llvm-objdump -t %p/Inputs/macho-invalid-getsection-index 2>&1 \ +RUN: | FileCheck -check-prefix INVALID-SECTION-IDX-GETSECT %s + +RUN: not llvm-objdump -t %p/Inputs/macho64-invalid-getsection-index 2>&1 \ +RUN: | FileCheck -check-prefix INVALID-SECTION-IDX-GETSECT64 %s + + +SMALL-LOADC-SIZE: Load command with size < 8 bytes +SMALL-SEGLOADC-SIZE: Segment load command size is too small +INCOMPLETE-LOADC: Malformed MachO file +TOO-MANY-SECTS: Number of sections too large for size of load command +BAD-SYMBOL: Requested symbol index is out of range +NAME-PAST-EOF: Symbol name entry points past end of file + +INVALID-SECTION-IDX-SEG-NAME: getSectionRawFinalSegmentName: Invalid section index +INVALID-SECTION-IDX-SECT-NAME: getSectionRawName: Invalid section index +INVALID-SECTION-IDX-GETSECT: getSection: Invalid section index +INVALID-SECTION-IDX-GETSECT64: getSection64: Invalid section index Index: llvm/trunk/test/Object/objdump-macho-quirks.test =================================================================== --- llvm/trunk/test/Object/objdump-macho-quirks.test +++ llvm/trunk/test/Object/objdump-macho-quirks.test @@ -1,9 +0,0 @@ -RUN: llvm-objdump -private-headers %p/Inputs/macho-zero-ncmds \ -RUN: | FileCheck %s -check-prefix A - -// Check that we don't get an infinite loop if ncmds = 0 -A: file format Mach-O 64-bit unknown -A: Mach header -A: magic cputype cpusubtype caps filetype ncmds sizeofcmds flags -A: MH_MAGIC_64 0x00 OBJECT 0 0 0x00000000 -