[Object, MachO] Don't crash on invalid MachO load commands.

Summary:
Currently all load commands are parsed in MachOObjectFile constructor.
If the next load command cannot be parsed, or if command size is too
small, properly report it through the error code and fail to construct
the object, instead of crashing the program.

Test Plan: regression test suite

Reviewers: rafael, filcab

Subscribers: llvm-commits
llvm-svn: 239080
This commit is contained in:
Alexey Samsonov 2015-06-04 19:57:46 +00:00
parent 9510d4e30b
commit c5843d04e0
4 changed files with 28 additions and 11 deletions

View File

@ -28,6 +28,7 @@ enum class object_error {
parse_failed,
unexpected_eof,
bitcode_section_not_found,
macho_small_load_command,
};
inline std::error_code make_error_code(object_error e) {

View File

@ -44,6 +44,8 @@ std::string _object_error_category::message(int EV) const {
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.");

View File

@ -194,24 +194,27 @@ static uint32_t getSectionFlags(const MachOObjectFile *O,
return Sect.flags;
}
static MachOObjectFile::LoadCommandInfo
static ErrorOr<MachOObjectFile::LoadCommandInfo>
getLoadCommandInfo(const MachOObjectFile *Obj, const char *Ptr) {
auto CmdOrErr = getStructOrErr<MachO::load_command>(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 = getStruct<MachO::load_command>(Obj, Load.Ptr);
if (Load.C.cmdsize < 8)
report_fatal_error("Load command with size < 8 bytes.");
Load.C = CmdOrErr.get();
return Load;
}
static MachOObjectFile::LoadCommandInfo
static ErrorOr<MachOObjectFile::LoadCommandInfo>
getFirstLoadCommandInfo(const MachOObjectFile *Obj) {
unsigned HeaderSize = Obj->is64Bit() ? sizeof(MachO::mach_header_64)
: sizeof(MachO::mach_header);
return getLoadCommandInfo(Obj, getPtr(Obj, HeaderSize));
}
static MachOObjectFile::LoadCommandInfo
static ErrorOr<MachOObjectFile::LoadCommandInfo>
getNextLoadCommandInfo(const MachOObjectFile *Obj,
const MachOObjectFile::LoadCommandInfo &L) {
return getLoadCommandInfo(Obj, L.Ptr + L.C.cmdsize);
@ -251,7 +254,12 @@ MachOObjectFile::MachOObjectFile(MemoryBufferRef Object, bool IsLittleEndian,
MachO::LoadCommandType SegmentLoadType = is64Bit() ?
MachO::LC_SEGMENT_64 : MachO::LC_SEGMENT;
LoadCommandInfo Load = getFirstLoadCommandInfo(this);
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) {
@ -318,8 +326,14 @@ MachOObjectFile::MachOObjectFile(MemoryBufferRef Object, bool IsLittleEndian,
Load.C.cmd == MachO::LC_LOAD_UPWARD_DYLIB) {
Libraries.push_back(Load.Ptr);
}
if (I < LoadCommandCount - 1)
Load = getNextLoadCommandInfo(this, Load);
if (I < LoadCommandCount - 1) {
auto LoadOrErr = getNextLoadCommandInfo(this, Load);
if (!LoadOrErr) {
EC = LoadOrErr.getError();
return;
}
Load = LoadOrErr.get();
}
}
assert(LoadCommands.size() == LoadCommandCount);
}

View File

@ -3,13 +3,13 @@ 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
INCOMPLETE-LOADC: Malformed MachO file
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: | 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
SMALL-LOADC-SIZE: Load command with size < 8 bytes
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