Index: include/llvm/Object/Error.h =================================================================== --- include/llvm/Object/Error.h +++ include/llvm/Object/Error.h @@ -28,6 +28,7 @@ parse_failed, unexpected_eof, bitcode_section_not_found, + macho_small_load_command, }; inline std::error_code make_error_code(object_error e) { Index: include/llvm/Object/MachO.h =================================================================== --- include/llvm/Object/MachO.h +++ include/llvm/Object/MachO.h @@ -429,10 +429,6 @@ } private: - // Walk load commands. - LoadCommandInfo getFirstLoadCommandInfo() const; - LoadCommandInfo getNextLoadCommandInfo(const LoadCommandInfo &L) const; - MachO::mach_header_64 Header64; MachO::mach_header *Header; // Points inside Header64. typedef SmallVector SectionList; Index: lib/Object/Error.cpp =================================================================== --- lib/Object/Error.cpp +++ lib/Object/Error.cpp @@ -44,6 +44,8 @@ return "The end of the file was unexpectedly encountered"; case object_error::bitcode_section_not_found: return "Bitcode section not found in object file"; + case object_error::macho_small_load_command: + return "Mach-O load command with size < 8 bytes"; } llvm_unreachable("An enumerator of object_error does not have a message " "defined."); Index: lib/Object/MachOObjectFile.cpp =================================================================== --- lib/Object/MachOObjectFile.cpp +++ lib/Object/MachOObjectFile.cpp @@ -204,6 +204,32 @@ EC = HeaderOrErr.getError(); } +static ErrorOr +getLoadCommandInfo(const MachOObjectFile *Obj, const char *Ptr) { + auto CmdOrErr = getStructOrErr(Obj, Ptr); + if (!CmdOrErr) + return CmdOrErr.getError(); + if (CmdOrErr->cmdsize < 8) + return object_error::macho_small_load_command; + MachOObjectFile::LoadCommandInfo Load; + Load.Ptr = Ptr; + Load.C = CmdOrErr.get(); + return Load; +} + +static ErrorOr +getFirstLoadCommandInfo(const MachOObjectFile *Obj) { + unsigned HeaderSize = Obj->is64Bit() ? sizeof(MachO::mach_header_64) + : sizeof(MachO::mach_header); + return getLoadCommandInfo(Obj, getPtr(Obj, HeaderSize)); +} + +static ErrorOr +getNextLoadCommandInfo(const MachOObjectFile *Obj, + const MachOObjectFile::LoadCommandInfo &L) { + return getLoadCommandInfo(Obj, L.Ptr + L.C.cmdsize); +} + MachOObjectFile::MachOObjectFile(MemoryBufferRef Object, bool IsLittleEndian, bool Is64bits, std::error_code &EC) : ObjectFile(getMachOType(IsLittleEndian, Is64bits), Object), @@ -227,7 +253,12 @@ MachO::LoadCommandType SegmentLoadType = is64Bit() ? MachO::LC_SEGMENT_64 : MachO::LC_SEGMENT; - LoadCommandInfo Load = getFirstLoadCommandInfo(); + auto LoadOrErr = getFirstLoadCommandInfo(this); + if (!LoadOrErr) { + EC = LoadOrErr.getError(); + return; + } + LoadCommandInfo Load = LoadOrErr.get(); for (unsigned I = 0; I < LoadCommandCount; ++I) { LoadCommands.push_back(Load); if (Load.C.cmd == MachO::LC_SYMTAB) { @@ -294,8 +325,14 @@ Load.C.cmd == MachO::LC_LOAD_UPWARD_DYLIB) { Libraries.push_back(Load.Ptr); } - if (I < LoadCommandCount - 1) - Load = getNextLoadCommandInfo(Load); + if (I < LoadCommandCount - 1) { + auto LoadOrErr = getNextLoadCommandInfo(this, Load); + if (!LoadOrErr) { + EC = LoadOrErr.getError(); + return; + } + Load = LoadOrErr.get(); + } } assert(LoadCommands.size() == LoadCommandCount); } @@ -1997,29 +2034,6 @@ return SectionRef(DRI, this); } -MachOObjectFile::LoadCommandInfo -MachOObjectFile::getFirstLoadCommandInfo() const { - MachOObjectFile::LoadCommandInfo Load; - - unsigned HeaderSize = is64Bit() ? sizeof(MachO::mach_header_64) : - 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; -} - -MachOObjectFile::LoadCommandInfo -MachOObjectFile::getNextLoadCommandInfo(const LoadCommandInfo &L) const { - 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 { assert(DRI.d.a < Sections.size() && "Should have detected this earlier"); return getStruct(this, Sections[DRI.d.a]); Index: test/Object/macho-invalid.test =================================================================== --- test/Object/macho-invalid.test +++ test/Object/macho-invalid.test @@ -1,41 +1,39 @@ // 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: llvm-objdump -private-headers %p/Inputs/macho64-invalid-incomplete-load-command 2>&1 \ RUN: | FileCheck -check-prefix INCOMPLETE-LOADC %s +INCOMPLETE-LOADC: Invalid data was encountered while parsing the file. -RUN: not llvm-objdump -private-headers %p/Inputs/macho-invalid-too-small-load-command 2>&1 \ +RUN: 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: llvm-objdump -private-headers %p/Inputs/macho64-invalid-too-small-load-command 2>&1 \ RUN: | FileCheck -check-prefix SMALL-LOADC-SIZE %s +SMALL-LOADC-SIZE: Mach-O load command with size < 8 bytes 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 +SMALL-SEGLOADC-SIZE: Segment load command size is too small 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 +TOO-MANY-SECTS: Number of sections too large for size of load command RUN: not llvm-objdump -t %p/Inputs/macho-invalid-bad-symbol-index 2>&1 \ RUN: | FileCheck -check-prefix BAD-SYMBOL %s +BAD-SYMBOL: Requested symbol index is out of range RUN: not llvm-objdump -t %p/Inputs/macho-invalid-symbol-name-past-eof 2>&1 \ RUN: | FileCheck -check-prefix NAME-PAST-EOF %s +NAME-PAST-EOF: Symbol name entry points before beginning or past end of file RUN: not llvm-nm %p/Inputs/macho-invalid-section-index-getSectionRawName 2>&1 \ RUN: | FileCheck -check-prefix INVALID-SECTION-IDX-SYMBOL-SEC %s +INVALID-SECTION-IDX-SYMBOL-SEC: getSymbolSection: Invalid section index RUN: llvm-objdump -private-headers %p/Inputs/macho-invalid-header 2>&1 | FileCheck -check-prefix INVALID-HEADER %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 before beginning or past end of file - -INVALID-SECTION-IDX-SYMBOL-SEC: getSymbolSection: Invalid section index INVALID-HEADER: Invalid data was encountered while parsing the file.