[Object, MachO] Don't crash on invalid MachO load commands.
authorAlexey Samsonov <vonosmas@gmail.com>
Thu, 4 Jun 2015 19:57:46 +0000 (19:57 +0000)
committerAlexey Samsonov <vonosmas@gmail.com>
Thu, 4 Jun 2015 19:57:46 +0000 (19:57 +0000)
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

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@239080 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/Object/Error.h
lib/Object/Error.cpp
lib/Object/MachOObjectFile.cpp
test/Object/macho-invalid.test

index 90c2bd74b43c5be7c420a5301bc946603b957e2b..13a14aa09c77604d1f921d5883913410173798c2 100644 (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) {
index d2daab72d589da5e4e8ff2f19f567985f47ea42e..1c8dad80b0a6db7c60cec7815238072099560e3f 100644 (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.");
index c1d138384441ffd3341ef716c95d6698f46ae25c..3262c6c26ab9ac60251b473a95ce5a12c16ea1ad 100644 (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);
 }
index fd09abf338bf4b06c4af3964283f35ce2655455f..55b186dedd30a4a3ba4e6a59ea21c4d67abb064c 100644 (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