From 7bcdeca5fcc897899ef693babec631f8b7216342 Mon Sep 17 00:00:00 2001 From: Kevin Enderby Date: Tue, 20 Sep 2016 20:14:14 +0000 Subject: [PATCH] Next set of additional error checks for invalid Mach-O files for bad load commands that use the Mach::dylib_command type for the load commands that are currently used in the MachOObjectFile constructor. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This contains the missing checks for LC_ID_DYLIB, LC_ID_DYLIB, etc. load commands and the fields for the Mach::dylib_command type. Also checks that only an MH_DYLIB or MH_STUB_DYLIB has an LC_ID_DYLIB load command (and others filetype don’t) and there is not more than one of these load commands. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@282008 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Object/MachOObjectFile.cpp | 96 +++++++++++++++--- .../macho-invalid-dylib-id-more-than-one | Bin 0 -> 84 bytes .../macho-invalid-dylib-name_offset-toobig | Bin 0 -> 52 bytes .../macho-invalid-dylib-name_offset-toosmall | Bin 0 -> 52 bytes .../Inputs/macho-invalid-dylib-name_toobig | Bin 0 -> 56 bytes test/Object/Inputs/macho-invalid-dylib-no-id | Bin 0 -> 28 bytes test/Object/Inputs/macho-invalid-dylib-small | Bin 0 -> 56 bytes .../Inputs/macho-invalid-dylib-wrong-filetype | Bin 0 -> 56 bytes test/Object/macho-invalid.test | 21 ++++ 9 files changed, 104 insertions(+), 13 deletions(-) create mode 100644 test/Object/Inputs/macho-invalid-dylib-id-more-than-one create mode 100644 test/Object/Inputs/macho-invalid-dylib-name_offset-toobig create mode 100644 test/Object/Inputs/macho-invalid-dylib-name_offset-toosmall create mode 100644 test/Object/Inputs/macho-invalid-dylib-name_toobig create mode 100644 test/Object/Inputs/macho-invalid-dylib-no-id create mode 100644 test/Object/Inputs/macho-invalid-dylib-small create mode 100644 test/Object/Inputs/macho-invalid-dylib-wrong-filetype diff --git a/lib/Object/MachOObjectFile.cpp b/lib/Object/MachOObjectFile.cpp index b57955b35dac..1592e49a10b4 100644 --- a/lib/Object/MachOObjectFile.cpp +++ b/lib/Object/MachOObjectFile.cpp @@ -579,6 +579,52 @@ static Error checkDyldInfoCommand(const MachOObjectFile *Obj, return Error::success(); } +static Error checkDylibCommand(const MachOObjectFile *Obj, + const MachOObjectFile::LoadCommandInfo &Load, + uint32_t LoadCommandIndex, const char *CmdName) { + if (Load.C.cmdsize < sizeof(MachO::dylib_command)) + return malformedError("load command " + Twine(LoadCommandIndex) + " " + + CmdName + " cmdsize too small"); + MachO::dylib_command D = getStruct(Obj, Load.Ptr); + if (D.dylib.name < sizeof(MachO::dylib_command)) + return malformedError("load command " + Twine(LoadCommandIndex) + " " + + CmdName + " name.offset field too small, not past " + "the end of the dylib_command struct"); + if (D.dylib.name >= D.cmdsize) + return malformedError("load command " + Twine(LoadCommandIndex) + " " + + CmdName + " name.offset field extends past the end " + "of the load command"); + // Make sure there is a null between the starting offset of the name and + // the end of the load command. + uint32_t i; + const char *P = (const char *)Load.Ptr; + for (i = D.dylib.name; i < D.cmdsize; i++) + if (P[i] == '\0') + break; + if (i >= D.cmdsize) + return malformedError("load command " + Twine(LoadCommandIndex) + " " + + CmdName + " library name extends past the end of the " + "load command"); + return Error::success(); +} + +static Error checkDylibIdCommand(const MachOObjectFile *Obj, + const MachOObjectFile::LoadCommandInfo &Load, + uint32_t LoadCommandIndex, + const char **LoadCmd) { + if (Error Err = checkDylibCommand(Obj, Load, LoadCommandIndex, + "LC_ID_DYLIB")) + return Err; + if (*LoadCmd != nullptr) + return malformedError("more than one LC_ID_DYLIB command"); + if (Obj->getHeader().filetype != MachO::MH_DYLIB && + Obj->getHeader().filetype != MachO::MH_DYLIB_STUB) + return malformedError("LC_ID_DYLIB load command in non-dynamic library " + "file type"); + *LoadCmd = Load.Ptr; + return Error::success(); +} + Expected> MachOObjectFile::create(MemoryBufferRef Object, bool IsLittleEndian, bool Is64Bits) { @@ -616,17 +662,17 @@ MachOObjectFile::MachOObjectFile(MemoryBufferRef Object, bool IsLittleEndian, } uint32_t LoadCommandCount = getHeader().ncmds; - if (LoadCommandCount == 0) - return; - LoadCommandInfo Load; - if (auto LoadOrErr = getFirstLoadCommandInfo(this)) - Load = *LoadOrErr; - else { - Err = LoadOrErr.takeError(); - return; + if (LoadCommandCount != 0) { + if (auto LoadOrErr = getFirstLoadCommandInfo(this)) + Load = *LoadOrErr; + else { + Err = LoadOrErr.takeError(); + return; + } } + const char *DyldIdLoadCmd = nullptr; for (unsigned I = 0; I < LoadCommandCount; ++I) { if (is64Bit()) { if (Load.C.cmdsize % 8 != 0) { @@ -689,11 +735,28 @@ MachOObjectFile::MachOObjectFile(MemoryBufferRef Object, bool IsLittleEndian, this, Load, Sections, HasPageZeroSegment, I, "LC_SEGMENT", SizeOfHeaders))) return; - } else if (Load.C.cmd == MachO::LC_LOAD_DYLIB || - Load.C.cmd == MachO::LC_LOAD_WEAK_DYLIB || - Load.C.cmd == MachO::LC_LAZY_LOAD_DYLIB || - Load.C.cmd == MachO::LC_REEXPORT_DYLIB || - Load.C.cmd == MachO::LC_LOAD_UPWARD_DYLIB) { + } else if (Load.C.cmd == MachO::LC_ID_DYLIB) { + if ((Err = checkDylibIdCommand(this, Load, I, &DyldIdLoadCmd))) + return; + } else if (Load.C.cmd == MachO::LC_LOAD_DYLIB) { + if ((Err = checkDylibCommand(this, Load, I, "LC_LOAD_DYLIB"))) + return; + Libraries.push_back(Load.Ptr); + } else if (Load.C.cmd == MachO::LC_LOAD_WEAK_DYLIB) { + if ((Err = checkDylibCommand(this, Load, I, "LC_LOAD_WEAK_DYLIB"))) + return; + Libraries.push_back(Load.Ptr); + } else if (Load.C.cmd == MachO::LC_LAZY_LOAD_DYLIB) { + if ((Err = checkDylibCommand(this, Load, I, "LC_LAZY_LOAD_DYLIB"))) + return; + Libraries.push_back(Load.Ptr); + } else if (Load.C.cmd == MachO::LC_REEXPORT_DYLIB) { + if ((Err = checkDylibCommand(this, Load, I, "LC_REEXPORT_DYLIB"))) + return; + Libraries.push_back(Load.Ptr); + } else if (Load.C.cmd == MachO::LC_LOAD_UPWARD_DYLIB) { + if ((Err = checkDylibCommand(this, Load, I, "LC_LOAD_UPWARD_DYLIB"))) + return; Libraries.push_back(Load.Ptr); } if (I < LoadCommandCount - 1) { @@ -754,6 +817,13 @@ MachOObjectFile::MachOObjectFile(MemoryBufferRef Object, bool IsLittleEndian, return; } } + if ((getHeader().filetype == MachO::MH_DYLIB || + getHeader().filetype == MachO::MH_DYLIB_STUB) && + DyldIdLoadCmd == nullptr) { + Err = malformedError("no LC_ID_DYLIB load command in dynamic library " + "filetype"); + return; + } assert(LoadCommands.size() == LoadCommandCount); Err = Error::success(); diff --git a/test/Object/Inputs/macho-invalid-dylib-id-more-than-one b/test/Object/Inputs/macho-invalid-dylib-id-more-than-one new file mode 100644 index 0000000000000000000000000000000000000000..3fcefdb9200a02c69fedf3a03f24735a71c14356 GIT binary patch literal 84 tcmX^2>+L^w1_lOZAZ7z%CLp!|Vi4d3Vi_Qo0CA8&T7EuGxunD*1^|Ln2KfL0 literal 0 HcmV?d00001 diff --git a/test/Object/Inputs/macho-invalid-dylib-name_offset-toobig b/test/Object/Inputs/macho-invalid-dylib-name_offset-toobig new file mode 100644 index 0000000000000000000000000000000000000000..3a5f0c7e50508de784030ce5824226a3c5563b5b GIT binary patch literal 52 hcmX^2>+L^w1_lOZAZCPO2_OvuK)M0MmICt-1OU(?1fl={ literal 0 HcmV?d00001 diff --git a/test/Object/Inputs/macho-invalid-dylib-name_offset-toosmall b/test/Object/Inputs/macho-invalid-dylib-name_offset-toosmall new file mode 100644 index 0000000000000000000000000000000000000000..fb3aa3b02a1ef176c0ff043b94b7ddf5d3c2ffe7 GIT binary patch literal 52 icmX^2>+L^w1_lOZAZCPO2_Ovu$_xw*AhsBohadpZ>IA6( literal 0 HcmV?d00001 diff --git a/test/Object/Inputs/macho-invalid-dylib-name_toobig b/test/Object/Inputs/macho-invalid-dylib-name_toobig new file mode 100644 index 0000000000000000000000000000000000000000..11ef9280eef1e63a97864de7bea0789598f4415d GIT binary patch literal 56 mcmX^2>+L^w1_lOZAZCPO2_Ovu3P3Ca#4tXP2?mKt$teKa76rBd literal 0 HcmV?d00001 diff --git a/test/Object/Inputs/macho-invalid-dylib-no-id b/test/Object/Inputs/macho-invalid-dylib-no-id new file mode 100644 index 0000000000000000000000000000000000000000..3715e8c26860eadade4cfe8ef4a17fccf4e5e9c5 GIT binary patch literal 28 WcmX^2>+L^w1_lOZAZ7z%WB>qTCIiF( literal 0 HcmV?d00001 diff --git a/test/Object/Inputs/macho-invalid-dylib-small b/test/Object/Inputs/macho-invalid-dylib-small new file mode 100644 index 0000000000000000000000000000000000000000..6aca91e6bca9632a9e906149beaeabd7b73f4dfa GIT binary patch literal 56 hcmX^2>+L^w1_lOZAZCPO5g-i$JU|R$3xhal007fo1P1^B literal 0 HcmV?d00001 diff --git a/test/Object/Inputs/macho-invalid-dylib-wrong-filetype b/test/Object/Inputs/macho-invalid-dylib-wrong-filetype new file mode 100644 index 0000000000000000000000000000000000000000..c9c862c6e8362abc633d77c4b78dabd4a6e343e5 GIT binary patch literal 56 ncmX^2>+L^w1_lOZAZ7w$Mj(~}Vi4d3Vh~#b#6bdS`S}b0*{ua) literal 0 HcmV?d00001 diff --git a/test/Object/macho-invalid.test b/test/Object/macho-invalid.test index 8868b88cff42..b8a1e10148f4 100644 --- a/test/Object/macho-invalid.test +++ b/test/Object/macho-invalid.test @@ -262,3 +262,24 @@ INVALID-DYLDINFO-EXPORT_OFF-EXPORT_SIZE: macho-invalid-dyldinfo-export_off-expor RUN: not llvm-objdump -macho -private-headers %p/Inputs/macho-invalid-dyldinfo-more-than-one 2>&1 | FileCheck -check-prefix INVALID-DYLDINFO-MORE-THAN-ONE %s INVALID-DYLDINFO-MORE-THAN-ONE: macho-invalid-dyldinfo-more-than-one': truncated or malformed object (more than one LC_DYLD_INFO and or LC_DYLD_INFO_ONLY command) + +RUN: not llvm-objdump -macho -private-headers %p/Inputs/macho-invalid-dylib-small 2>&1 | FileCheck -check-prefix INVALID-DYLIB-SMALL %s +INVALID-DYLIB-SMALL: macho-invalid-dylib-small': truncated or malformed object (load command 0 LC_LOAD_DYLIB cmdsize too small) + +RUN: not llvm-objdump -macho -private-headers %p/Inputs/macho-invalid-dylib-name_offset-toobig 2>&1 | FileCheck -check-prefix INVALID-DYLIB-NAME_OFFSET-TOOBIG %s +INVALID-DYLIB-NAME_OFFSET-TOOBIG: macho-invalid-dylib-name_offset-toobig': truncated or malformed object (load command 0 LC_LOAD_WEAK_DYLIB name.offset field extends past the end of the load command) + +RUN: not llvm-objdump -macho -private-headers %p/Inputs/macho-invalid-dylib-name_toobig 2>&1 | FileCheck -check-prefix INVALID-DYLIB-NAME_TOOBIG %s +INVALID-DYLIB-NAME_TOOBIG: macho-invalid-dylib-name_toobig': truncated or malformed object (load command 0 LC_LAZY_LOAD_DYLIB library name extends past the end of the load command) + +RUN: not llvm-objdump -macho -private-headers %p/Inputs/macho-invalid-dylib-name_offset-toosmall 2>&1 | FileCheck -check-prefix INVALID-DYLIB-NAME_OFFSET-TOOSMALL %s +INVALID-DYLIB-NAME_OFFSET-TOOSMALL: macho-invalid-dylib-name_offset-toosmall': truncated or malformed object (load command 0 LC_LOAD_UPWARD_DYLIB name.offset field too small, not past the end of the dylib_command struct) + +RUN: not llvm-objdump -macho -private-headers %p/Inputs/macho-invalid-dylib-id-more-than-one 2>&1 | FileCheck -check-prefix INVALID-DYLIB-ID-MORE-THAN-ONE %s +INVALID-DYLIB-ID-MORE-THAN-ONE: macho-invalid-dylib-id-more-than-one': truncated or malformed object (more than one LC_ID_DYLIB command) + +RUN: not llvm-objdump -macho -private-headers %p/Inputs/macho-invalid-dylib-wrong-filetype 2>&1 | FileCheck -check-prefix INVALID-DYLIB-WRONG-FILETYPE %s +INVALID-DYLIB-WRONG-FILETYPE: macho-invalid-dylib-wrong-filetype': truncated or malformed object (LC_ID_DYLIB load command in non-dynamic library file type) + +RUN: not llvm-objdump -macho -private-headers %p/Inputs/macho-invalid-dylib-no-id 2>&1 | FileCheck -check-prefix INVALID-DYLIB-NO-ID %s +INVALID-DYLIB-NO-ID: macho-invalid-dylib-no-id': truncated or malformed object (no LC_ID_DYLIB load command in dynamic library filetype)