From 210030ba95df715ec32d1dd2918f2ef8206817f7 Mon Sep 17 00:00:00 2001 From: Kevin Enderby Date: Wed, 19 Oct 2016 23:44:34 +0000 Subject: [PATCH] Next set of additional error checks for invalid Mach-O files for the 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 | 175 +++++++++++++++++++++ llvm/lib/Object/MachOObjectFile.cpp | 113 +++++++++++++ .../Inputs/macho-invalid-thread-count-pastend | Bin 0 -> 40 bytes .../Object/Inputs/macho-invalid-thread-count-wrong | Bin 0 -> 48 bytes .../Inputs/macho-invalid-thread-flavor-unknown | Bin 0 -> 48 bytes .../Inputs/macho-invalid-thread-state-pastend | Bin 0 -> 48 bytes .../Inputs/macho-invalid-thread-unknown-cputype | Bin 0 -> 48 bytes .../Inputs/macho-invalid-unixthread-more-than-one | Bin 0 -> 396 bytes llvm/test/Object/macho-invalid.test | 18 +++ 9 files changed, 306 insertions(+) create mode 100644 llvm/test/Object/Inputs/macho-invalid-thread-count-pastend create mode 100644 llvm/test/Object/Inputs/macho-invalid-thread-count-wrong create mode 100644 llvm/test/Object/Inputs/macho-invalid-thread-flavor-unknown create mode 100644 llvm/test/Object/Inputs/macho-invalid-thread-state-pastend create mode 100644 llvm/test/Object/Inputs/macho-invalid-thread-unknown-cputype create mode 100644 llvm/test/Object/Inputs/macho-invalid-unixthread-more-than-one diff --git a/llvm/include/llvm/Support/MachO.h b/llvm/include/llvm/Support/MachO.h index ebaaa7e..c56a6aa 100644 --- a/llvm/include/llvm/Support/MachO.h +++ b/llvm/include/llvm/Support/MachO.h @@ -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; diff --git a/llvm/lib/Object/MachOObjectFile.cpp b/llvm/lib/Object/MachOObjectFile.cpp index a6cdc288..87b19a1 100644 --- a/llvm/lib/Object/MachOObjectFile.cpp +++ b/llvm/lib/Object/MachOObjectFile.cpp @@ -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(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> 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 index 0000000000000000000000000000000000000000..4511c04adfc8236fe62c7dd4f4d9e8722fc4c394 GIT binary patch literal 40 dcmX^2>+L^w1_nlE1|R{&JU|)*Sb-SE1^|(d1Kt1t literal 0 HcmV?d00001 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 index 0000000000000000000000000000000000000000..8a96b9ddfc9e4df5bd65754b383a2035efe77f0a GIT binary patch literal 48 hcmX^2>+L^w1_nlE1|R{&0zeuBSb!MB2GJl40sygV1ML6+ literal 0 HcmV?d00001 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 index 0000000000000000000000000000000000000000..72a680c39bf7c92886e2d41237d79d57ba6cb6a7 GIT binary patch literal 48 jcmX^2>+L^w1_nlE1|R{&0zeuBSb!MB{tc7>u|WU;y$S^7 literal 0 HcmV?d00001 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 index 0000000000000000000000000000000000000000..051390f24b611d601e9f700433f52d0929fd79b8 GIT binary patch literal 48 icmX^2>+L^w1_nlE1|R{&0zeuBSb!MB2GLqT3<3bMn*+L@eMg|6E1_lNo1;zqE8U$E?7{mtAT0jf}0KLfsQ~&?~ literal 0 HcmV?d00001 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 index 0000000000000000000000000000000000000000..7ce66599bb97c619f4d06f5107ea7a202a7b7ef2 GIT binary patch literal 396 qcmX^2>+L^w1_nlE1|R{%Oh8-!VgLav5bpqD79iFF;-LbDs=EPU8U_gf literal 0 HcmV?d00001 diff --git a/llvm/test/Object/macho-invalid.test b/llvm/test/Object/macho-invalid.test index cde4520..111ba66 100644 --- a/llvm/test/Object/macho-invalid.test +++ b/llvm/test/Object/macho-invalid.test @@ -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) -- 2.7.4