[Object, MachO] Don't crash on parsing invalid MachO header.
authorAlexey Samsonov <vonosmas@gmail.com>
Thu, 4 Jun 2015 19:45:22 +0000 (19:45 +0000)
committerAlexey Samsonov <vonosmas@gmail.com>
Thu, 4 Jun 2015 19:45:22 +0000 (19:45 +0000)
Summary: Instead, properly report this error from MachOObjectFile constructor.

Test Plan: regression test suite

Reviewers: rafael

Subscribers: llvm-commits

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

lib/Object/MachOObjectFile.cpp
test/Object/Inputs/macho-invalid-header [new file with mode: 0644]
test/Object/macho-invalid.test

index 1620c1a6a915841b7cd755136a46b7a2cc850978..c1d138384441ffd3341ef716c95d6698f46ae25c 100644 (file)
@@ -38,6 +38,7 @@ namespace {
   };
 }
 
+// FIXME: Replace all uses of this function with getStructOrErr.
 template <typename T>
 static T getStruct(const MachOObjectFile *O, const char *P) {
   // Don't read before the beginning or past the end of the file
@@ -51,6 +52,19 @@ static T getStruct(const MachOObjectFile *O, const char *P) {
   return Cmd;
 }
 
+template <typename T>
+static ErrorOr<T> getStructOrErr(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())
+    return object_error::parse_failed;
+
+  T Cmd;
+  memcpy(&Cmd, P, sizeof(T));
+  if (O->isLittleEndian() != sys::IsLittleEndianHost)
+    MachO::swapStruct(Cmd);
+  return Cmd;
+}
+
 template <typename SegmentCmd>
 static uint32_t getSegmentLoadCommandNumSections(const SegmentCmd &S,
                                                  uint32_t Cmdsize) {
@@ -203,6 +217,16 @@ getNextLoadCommandInfo(const MachOObjectFile *Obj,
   return getLoadCommandInfo(Obj, L.Ptr + L.C.cmdsize);
 }
 
+template <typename T>
+static void parseHeader(const MachOObjectFile *Obj, T &Header,
+                        std::error_code &EC) {
+  auto HeaderOrErr = getStructOrErr<T>(Obj, getPtr(Obj, 0));
+  if (HeaderOrErr)
+    Header = HeaderOrErr.get();
+  else
+    EC = HeaderOrErr.getError();
+}
+
 MachOObjectFile::MachOObjectFile(MemoryBufferRef Object, bool IsLittleEndian,
                                  bool Is64bits, std::error_code &EC)
     : ObjectFile(getMachOType(IsLittleEndian, Is64bits), Object),
@@ -210,14 +234,15 @@ MachOObjectFile::MachOObjectFile(MemoryBufferRef Object, bool IsLittleEndian,
       DataInCodeLoadCmd(nullptr), LinkOptHintsLoadCmd(nullptr),
       DyldInfoLoadCmd(nullptr), UuidLoadCmd(nullptr),
       HasPageZeroSegment(false) {
-  // Parse header.
   if (is64Bit())
-    Header64 = getStruct<MachO::mach_header_64>(this, getPtr(this, 0));
+    parseHeader(this, Header64, EC);
   else
     // First fields of MachO::mach_header_64 are the same as
     // in MachO::mach_header.
-    *reinterpret_cast<MachO::mach_header *>(&this->Header64) =
-        getStruct<MachO::mach_header>(this, getPtr(this, 0));
+    parseHeader(this, *reinterpret_cast<MachO::mach_header *>(&this->Header64),
+                EC);
+  if (EC)
+    return;
 
   uint32_t LoadCommandCount = getHeader().ncmds;
   if (LoadCommandCount == 0)
diff --git a/test/Object/Inputs/macho-invalid-header b/test/Object/Inputs/macho-invalid-header
new file mode 100644 (file)
index 0000000..da52d43
Binary files /dev/null and b/test/Object/Inputs/macho-invalid-header differ
index 8af30b6c8446fab40a86419393f5fe8c9ad5ce8d..fd09abf338bf4b06c4af3964283f35ce2655455f 100644 (file)
@@ -34,3 +34,6 @@ 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: not llvm-objdump -private-headers %p/Inputs/macho-invalid-header 2>&1 | FileCheck -check-prefix INVALID-HEADER %s
+INVALID-HEADER: Invalid data was encountered while parsing the file