Next set of additional error checks for invalid Mach-O files for the
authorKevin Enderby <enderby@apple.com>
Wed, 19 Oct 2016 23:44:34 +0000 (23:44 +0000)
committerKevin Enderby <enderby@apple.com>
Wed, 19 Oct 2016 23:44:34 +0000 (23:44 +0000)
load commands that use the MachO::thread_command type
but are not used in llvm libObject code but used in llvm tool code.

This includes the LC_UNIXTHREAD and LC_THREAD
load commands.

A quick note about the philosophy of the error checking in
libObject for Mach-O files, the idea behind the checking is
that we never will return a Mach-O file out of libObject that
contains unknown things in the load commands.

To do this the 32-bit ARM and PPC general tread states
needed to be defined as two test case binaries contained
them.  If other thread states for other CPUs need to be
added we will do that as needed.

Going forward the LC_MAIN load command is used to
set the entry point in Mach-O executables these days
instead of an LC_UNIXTHREAD as was done in the past.
So today only in core files are LC_THREAD load commands
and thread states usually found.

Other thread states have not yet been defined in
include/Support/MachO.h at this time.  But that can be
added as needed with their corresponding checking also
added.

llvm-svn: 284668

llvm/include/llvm/Support/MachO.h
llvm/lib/Object/MachOObjectFile.cpp
llvm/test/Object/Inputs/macho-invalid-thread-count-pastend [new file with mode: 0644]
llvm/test/Object/Inputs/macho-invalid-thread-count-wrong [new file with mode: 0644]
llvm/test/Object/Inputs/macho-invalid-thread-flavor-unknown [new file with mode: 0644]
llvm/test/Object/Inputs/macho-invalid-thread-state-pastend [new file with mode: 0644]
llvm/test/Object/Inputs/macho-invalid-thread-unknown-cputype [new file with mode: 0644]
llvm/test/Object/Inputs/macho-invalid-unixthread-more-than-one [new file with mode: 0644]
llvm/test/Object/macho-invalid.test

index ebaaa7e..c56a6aa 100644 (file)
@@ -1710,6 +1710,181 @@ namespace llvm {
     const uint32_t x86_EXCEPTION_STATE_COUNT =
       sizeof(x86_exception_state_t) / sizeof(uint32_t);
 
+    struct arm_thread_state32_t {
+      uint32_t r[13];
+      uint32_t sp;
+      uint32_t lr;
+      uint32_t pc;
+      uint32_t cpsr;
+    };
+
+    inline void swapStruct(arm_thread_state32_t &x) {
+      for (int i = 0; i < 13; i++)
+        sys::swapByteOrder(x.r[i]);
+      sys::swapByteOrder(x.sp);
+      sys::swapByteOrder(x.lr);
+      sys::swapByteOrder(x.pc);
+      sys::swapByteOrder(x.cpsr);
+    }
+
+    struct arm_state_hdr_t {
+      uint32_t flavor;
+      uint32_t count;
+    };
+
+    struct arm_thread_state_t {
+      arm_state_hdr_t tsh;
+      union {
+        arm_thread_state32_t ts32;
+      } uts;
+    };
+
+    inline void swapStruct(arm_state_hdr_t &x) {
+      sys::swapByteOrder(x.flavor);
+      sys::swapByteOrder(x.count);
+    }
+
+    enum ARMThreadFlavors {
+      ARM_THREAD_STATE      = 1,
+      ARM_VFP_STATE         = 2,
+      ARM_EXCEPTION_STATE   = 3,
+      ARM_DEBUG_STATE       = 4,
+      ARN_THREAD_STATE_NONE = 5,
+      ARM_THREAD_STATE64    = 6,
+      ARM_EXCEPTION_STATE64 = 7
+    };
+
+    inline void swapStruct(arm_thread_state_t &x) {
+      swapStruct(x.tsh);
+      if (x.tsh.flavor == ARM_THREAD_STATE)
+        swapStruct(x.uts.ts32);
+    }
+
+    const uint32_t ARM_THREAD_STATE_COUNT =
+      sizeof(arm_thread_state32_t) / sizeof(uint32_t);
+
+    struct ppc_thread_state32_t {
+      uint32_t srr0;
+      uint32_t srr1;
+      uint32_t r0;
+      uint32_t r1;
+      uint32_t r2;
+      uint32_t r3;
+      uint32_t r4;
+      uint32_t r5;
+      uint32_t r6;
+      uint32_t r7;
+      uint32_t r8;
+      uint32_t r9;
+      uint32_t r10;
+      uint32_t r11;
+      uint32_t r12;
+      uint32_t r13;
+      uint32_t r14;
+      uint32_t r15;
+      uint32_t r16;
+      uint32_t r17;
+      uint32_t r18;
+      uint32_t r19;
+      uint32_t r20;
+      uint32_t r21;
+      uint32_t r22;
+      uint32_t r23;
+      uint32_t r24;
+      uint32_t r25;
+      uint32_t r26;
+      uint32_t r27;
+      uint32_t r28;
+      uint32_t r29;
+      uint32_t r30;
+      uint32_t r31;
+      uint32_t ct;
+      uint32_t xer;
+      uint32_t lr;
+      uint32_t ctr;
+      uint32_t mq;
+      uint32_t vrsave;
+    };
+
+    inline void swapStruct(ppc_thread_state32_t &x) {
+      sys::swapByteOrder(x.srr0);
+      sys::swapByteOrder(x.srr1);
+      sys::swapByteOrder(x.r0);
+      sys::swapByteOrder(x.r1);
+      sys::swapByteOrder(x.r2);
+      sys::swapByteOrder(x.r3);
+      sys::swapByteOrder(x.r4);
+      sys::swapByteOrder(x.r5);
+      sys::swapByteOrder(x.r6);
+      sys::swapByteOrder(x.r7);
+      sys::swapByteOrder(x.r8);
+      sys::swapByteOrder(x.r9);
+      sys::swapByteOrder(x.r10);
+      sys::swapByteOrder(x.r11);
+      sys::swapByteOrder(x.r12);
+      sys::swapByteOrder(x.r13);
+      sys::swapByteOrder(x.r14);
+      sys::swapByteOrder(x.r15);
+      sys::swapByteOrder(x.r16);
+      sys::swapByteOrder(x.r17);
+      sys::swapByteOrder(x.r18);
+      sys::swapByteOrder(x.r19);
+      sys::swapByteOrder(x.r20);
+      sys::swapByteOrder(x.r21);
+      sys::swapByteOrder(x.r22);
+      sys::swapByteOrder(x.r23);
+      sys::swapByteOrder(x.r24);
+      sys::swapByteOrder(x.r25);
+      sys::swapByteOrder(x.r26);
+      sys::swapByteOrder(x.r27);
+      sys::swapByteOrder(x.r28);
+      sys::swapByteOrder(x.r29);
+      sys::swapByteOrder(x.r30);
+      sys::swapByteOrder(x.r31);
+      sys::swapByteOrder(x.ct);
+      sys::swapByteOrder(x.xer);
+      sys::swapByteOrder(x.lr);
+      sys::swapByteOrder(x.ctr);
+      sys::swapByteOrder(x.mq);
+      sys::swapByteOrder(x.vrsave);
+    }
+
+    struct ppc_state_hdr_t {
+      uint32_t flavor;
+      uint32_t count;
+    };
+
+    struct ppc_thread_state_t {
+      ppc_state_hdr_t tsh;
+      union {
+        ppc_thread_state32_t ts32;
+      } uts;
+    };
+
+    inline void swapStruct(ppc_state_hdr_t &x) {
+      sys::swapByteOrder(x.flavor);
+      sys::swapByteOrder(x.count);
+    }
+
+    enum PPCThreadFlavors {
+      PPC_THREAD_STATE      = 1,
+      PPC_FLOAT_STATE       = 2,
+      PPC_EXCEPTION_STATE   = 3,
+      PPC_VECTOR_STATE      = 4,
+      PPC_THREAD_STATE64    = 5,
+      PPC_EXCEPTION_STATE64 = 6,
+      PPC_THREAD_STATE_NONE = 7
+    };
+
+    inline void swapStruct(ppc_thread_state_t &x) {
+      swapStruct(x.tsh);
+      if (x.tsh.flavor == PPC_THREAD_STATE)
+        swapStruct(x.uts.ts32);
+    }
+
+    const uint32_t PPC_THREAD_STATE_COUNT =
+      sizeof(ppc_thread_state32_t) / sizeof(uint32_t);
+
     // Define a union of all load command structs
     #define LOAD_COMMAND_STRUCT(LCStruct) LCStruct LCStruct##_data;
 
index a6cdc28..87b19a1 100644 (file)
@@ -782,6 +782,107 @@ static Error checkSubCommand(const MachOObjectFile *Obj,
   return Error::success();
 }
 
+static Error checkThreadCommand(const MachOObjectFile *Obj,
+                                const MachOObjectFile::LoadCommandInfo &Load,
+                                uint32_t LoadCommandIndex,
+                                const char *CmdName) {
+  if (Load.C.cmdsize < sizeof(MachO::thread_command))
+    return malformedError("load command " + Twine(LoadCommandIndex) +
+                          CmdName + " cmdsize too small");
+  MachO::thread_command T =
+    getStruct<MachO::thread_command>(Obj, Load.Ptr);
+  const char *state = Load.Ptr + sizeof(MachO::thread_command);
+  const char *end = Load.Ptr + T.cmdsize;
+  uint32_t nflavor = 0;
+  uint32_t cputype = getCPUType(Obj);
+  while (state < end) {
+    if(state + sizeof(uint32_t) > end)
+      return malformedError("load command " + Twine(LoadCommandIndex) +
+                            "flavor in " + CmdName + " extends past end of "
+                            "command");
+    uint32_t flavor;
+    memcpy(&flavor, state, sizeof(uint32_t));
+    if (Obj->isLittleEndian() != sys::IsLittleEndianHost)
+      sys::swapByteOrder(flavor);
+    state += sizeof(uint32_t);
+
+    if(state + sizeof(uint32_t) > end)
+      return malformedError("load command " + Twine(LoadCommandIndex) +
+                            " count in " + CmdName + " extends past end of "
+                            "command");
+    uint32_t count;
+    memcpy(&count, state, sizeof(uint32_t));
+    if (Obj->isLittleEndian() != sys::IsLittleEndianHost)
+      sys::swapByteOrder(count);
+    state += sizeof(uint32_t);
+
+    if (cputype == MachO::CPU_TYPE_X86_64) {
+      if (flavor == MachO::x86_THREAD_STATE64) {
+        if (count != MachO::x86_THREAD_STATE64_COUNT)
+          return malformedError("load command " + Twine(LoadCommandIndex) +
+                                " count not x86_THREAD_STATE64_COUNT for "
+                                "flavor number " + Twine(nflavor) + " which is "
+                                "a x86_THREAD_STATE64 flavor in " + CmdName +
+                                " command");
+        if (state + sizeof(MachO::x86_thread_state64_t) > end)
+          return malformedError("load command " + Twine(LoadCommandIndex) +
+                                " x86_THREAD_STATE64 extends past end of "
+                                "command in " + CmdName + " command");
+        state += sizeof(MachO::x86_thread_state64_t);
+      } else {
+        return malformedError("load command " + Twine(LoadCommandIndex) +
+                              " unknown flavor (" + Twine(flavor) + ") for "
+                              "flavor number " + Twine(nflavor) + " in " +
+                              CmdName + " command");
+      }
+    } else if (cputype == MachO::CPU_TYPE_ARM) {
+      if (flavor == MachO::ARM_THREAD_STATE) {
+        if (count != MachO::ARM_THREAD_STATE_COUNT)
+          return malformedError("load command " + Twine(LoadCommandIndex) +
+                                " count not ARM_THREAD_STATE_COUNT for "
+                                "flavor number " + Twine(nflavor) + " which is "
+                                "a ARM_THREAD_STATE flavor in " + CmdName +
+                                " command");
+        if (state + sizeof(MachO::arm_thread_state32_t) > end)
+          return malformedError("load command " + Twine(LoadCommandIndex) +
+                                " ARM_THREAD_STATE extends past end of "
+                                "command in " + CmdName + " command");
+        state += sizeof(MachO::arm_thread_state32_t);
+      } else {
+        return malformedError("load command " + Twine(LoadCommandIndex) +
+                              " unknown flavor (" + Twine(flavor) + ") for "
+                              "flavor number " + Twine(nflavor) + " in " +
+                              CmdName + " command");
+      }
+    } else if (cputype == MachO::CPU_TYPE_POWERPC) {
+      if (flavor == MachO::PPC_THREAD_STATE) {
+        if (count != MachO::PPC_THREAD_STATE_COUNT)
+          return malformedError("load command " + Twine(LoadCommandIndex) +
+                                " count not PPC_THREAD_STATE_COUNT for "
+                                "flavor number " + Twine(nflavor) + " which is "
+                                "a PPC_THREAD_STATE flavor in " + CmdName +
+                                " command");
+        if (state + sizeof(MachO::ppc_thread_state32_t) > end)
+          return malformedError("load command " + Twine(LoadCommandIndex) +
+                                " PPC_THREAD_STATE extends past end of "
+                                "command in " + CmdName + " command");
+        state += sizeof(MachO::ppc_thread_state32_t);
+      } else {
+        return malformedError("load command " + Twine(LoadCommandIndex) +
+                              " unknown flavor (" + Twine(flavor) + ") for "
+                              "flavor number " + Twine(nflavor) + " in " +
+                              CmdName + " command");
+      }
+    } else {
+      return malformedError("unknown cputype (" + Twine(cputype) + ") load "
+                            "command " + Twine(LoadCommandIndex) + " for " +
+                            CmdName + " command can't be checked");
+    }
+    nflavor++;
+  }
+  return Error::success();
+}
+
 Expected<std::unique_ptr<MachOObjectFile>>
 MachOObjectFile::create(MemoryBufferRef Object, bool IsLittleEndian,
                         bool Is64Bits) {
@@ -839,6 +940,7 @@ MachOObjectFile::MachOObjectFile(MemoryBufferRef Object, bool IsLittleEndian,
   const char *EntryPointLoadCmd = nullptr;
   const char *EncryptLoadCmd = nullptr;
   const char *RoutinesLoadCmd = nullptr;
+  const char *UnixThreadLoadCmd = nullptr;
   for (unsigned I = 0; I < LoadCommandCount; ++I) {
     if (is64Bit()) {
       if (Load.C.cmdsize % 8 != 0) {
@@ -1094,6 +1196,17 @@ MachOObjectFile::MachOObjectFile(MemoryBufferRef Object, bool IsLittleEndian,
         return;
       }
       RoutinesLoadCmd = Load.Ptr;
+    } else if (Load.C.cmd == MachO::LC_UNIXTHREAD) {
+      if ((Err = checkThreadCommand(this, Load, I, "LC_UNIXTHREAD")))
+        return;
+      if (UnixThreadLoadCmd) {
+        Err = malformedError("more than one LC_UNIXTHREAD command");
+        return;
+      }
+      UnixThreadLoadCmd = Load.Ptr;
+    } else if (Load.C.cmd == MachO::LC_THREAD) {
+      if ((Err = checkThreadCommand(this, Load, I, "LC_THREAD")))
+        return;
     }
     if (I < LoadCommandCount - 1) {
       if (auto LoadOrErr = getNextLoadCommandInfo(this, I, Load))
diff --git a/llvm/test/Object/Inputs/macho-invalid-thread-count-pastend b/llvm/test/Object/Inputs/macho-invalid-thread-count-pastend
new file mode 100644 (file)
index 0000000..4511c04
Binary files /dev/null and b/llvm/test/Object/Inputs/macho-invalid-thread-count-pastend differ
diff --git a/llvm/test/Object/Inputs/macho-invalid-thread-count-wrong b/llvm/test/Object/Inputs/macho-invalid-thread-count-wrong
new file mode 100644 (file)
index 0000000..8a96b9d
Binary files /dev/null and b/llvm/test/Object/Inputs/macho-invalid-thread-count-wrong differ
diff --git a/llvm/test/Object/Inputs/macho-invalid-thread-flavor-unknown b/llvm/test/Object/Inputs/macho-invalid-thread-flavor-unknown
new file mode 100644 (file)
index 0000000..72a680c
Binary files /dev/null and b/llvm/test/Object/Inputs/macho-invalid-thread-flavor-unknown differ
diff --git a/llvm/test/Object/Inputs/macho-invalid-thread-state-pastend b/llvm/test/Object/Inputs/macho-invalid-thread-state-pastend
new file mode 100644 (file)
index 0000000..051390f
Binary files /dev/null and b/llvm/test/Object/Inputs/macho-invalid-thread-state-pastend differ
diff --git a/llvm/test/Object/Inputs/macho-invalid-thread-unknown-cputype b/llvm/test/Object/Inputs/macho-invalid-thread-unknown-cputype
new file mode 100644 (file)
index 0000000..15dae3e
Binary files /dev/null and b/llvm/test/Object/Inputs/macho-invalid-thread-unknown-cputype differ
diff --git a/llvm/test/Object/Inputs/macho-invalid-unixthread-more-than-one b/llvm/test/Object/Inputs/macho-invalid-unixthread-more-than-one
new file mode 100644 (file)
index 0000000..7ce6659
Binary files /dev/null and b/llvm/test/Object/Inputs/macho-invalid-unixthread-more-than-one differ
index cde4520..111ba66 100644 (file)
@@ -376,3 +376,21 @@ INVALID-ROUTINES64-MORE-THAN-ONE: macho-invalid-routines64-more-than-one': trunc
 
 RUN: not llvm-objdump -macho -private-headers %p/Inputs/macho-invalid-codesign-bad-size 2>&1 | FileCheck -check-prefix INVALID-CODESIGN-BAD-SIZE %s
 INVALID-CODESIGN-BAD-SIZE: macho-invalid-codesign-bad-size': truncated or malformed object (LC_CODE_SIGNATURE command 0 has incorrect cmdsize)
+
+RUN: not llvm-objdump -macho -private-headers %p/Inputs/macho-invalid-thread-count-pastend 2>&1 | FileCheck -check-prefix INVALID-THREAD-COUNT-PASTEND %s
+INVALID-THREAD-COUNT-PASTEND: macho-invalid-thread-count-pastend': truncated or malformed object (load command 0 count in LC_UNIXTHREAD extends past end of command)
+
+RUN: not llvm-objdump -macho -private-headers %p/Inputs/macho-invalid-thread-count-wrong 2>&1 | FileCheck -check-prefix INVALID-THREAD-COUNT-WRONG %s
+INVALID-THREAD-COUNT-WRONG: macho-invalid-thread-count-wrong': truncated or malformed object (load command 0 count not x86_THREAD_STATE64_COUNT for flavor number 0 which is a x86_THREAD_STATE64 flavor in LC_THREAD command)
+
+RUN: not llvm-objdump -macho -private-headers %p/Inputs/macho-invalid-thread-flavor-unknown 2>&1 | FileCheck -check-prefix INVALID-THREAD-FLAVOR-UNKNOWN %s
+INVALID-THREAD-FLAVOR-UNKNOWN: macho-invalid-thread-flavor-unknown': truncated or malformed object (load command 0 unknown flavor (507) for flavor number 0 in LC_THREAD command)
+
+RUN: not llvm-objdump -macho -private-headers %p/Inputs/macho-invalid-thread-state-pastend 2>&1 | FileCheck -check-prefix INVALID-THREAD-PASTEND %s
+INVALID-THREAD-PASTEND: macho-invalid-thread-state-pastend': truncated or malformed object (load command 0 x86_THREAD_STATE64 extends past end of command in LC_THREAD command)
+
+RUN: not llvm-objdump -macho -private-headers %p/Inputs/macho-invalid-thread-unknown-cputype 2>&1 | FileCheck -check-prefix INVALID-THREAD-UNKNOWN-CPUTYPE %s
+INVALID-THREAD-UNKNOWN-CPUTYPE: macho-invalid-thread-unknown-cputype': truncated or malformed object (unknown cputype (328) load command 0 for LC_THREAD command can't be checked)
+
+RUN: not llvm-objdump -macho -private-headers %p/Inputs/macho-invalid-unixthread-more-than-one 2>&1 | FileCheck -check-prefix INVALID-UNIXTHREAD-MORE-THAN-ONE %s
+INVALID-UNIXTHREAD-MORE-THAN-ONE: macho-invalid-unixthread-more-than-one': truncated or malformed object (more than one LC_UNIXTHREAD command)