diff options
17 files changed, 218 insertions, 31 deletions
diff --git a/llvm/include/llvm/Object/MachO.h b/llvm/include/llvm/Object/MachO.h index 2c3c89d1054..f2b273c82a9 100644 --- a/llvm/include/llvm/Object/MachO.h +++ b/llvm/include/llvm/Object/MachO.h @@ -66,11 +66,13 @@ using dice_iterator = content_iterator<DiceRef>; /// ExportEntry encapsulates the current-state-of-the-walk used when doing a /// non-recursive walk of the trie data structure. This allows you to iterate /// across all exported symbols using: -/// for (const llvm::object::ExportEntry &AnExport : Obj->exports()) { +/// Error Err; +/// for (const llvm::object::ExportEntry &AnExport : Obj->exports(&Err)) { /// } +/// if (Err) { report error ... class ExportEntry { public: - ExportEntry(ArrayRef<uint8_t> Trie); + ExportEntry(Error *Err, const MachOObjectFile *O, ArrayRef<uint8_t> Trie); StringRef name() const; uint64_t flags() const; @@ -88,7 +90,7 @@ private: void moveToFirst(); void moveToEnd(); - uint64_t readULEB128(const uint8_t *&p); + uint64_t readULEB128(const uint8_t *&p, const char **error); void pushDownUntilBottom(); void pushNode(uint64_t Offset); @@ -107,12 +109,19 @@ private: unsigned ParentStringLength = 0; bool IsExportNode = false; }; + using NodeList = SmallVector<NodeState, 16>; + using node_iterator = NodeList::const_iterator; + Error *E; + const MachOObjectFile *O; ArrayRef<uint8_t> Trie; SmallString<256> CumulativeString; - SmallVector<NodeState, 16> Stack; - bool Malformed = false; + NodeList Stack; bool Done = false; + + iterator_range<node_iterator> nodes() const { + return make_range(Stack.begin(), Stack.end()); + } }; using export_iterator = content_iterator<ExportEntry>; @@ -356,10 +365,14 @@ public: iterator_range<load_command_iterator> load_commands() const; /// For use iterating over all exported symbols. - iterator_range<export_iterator> exports() const; + iterator_range<export_iterator> exports(Error &Err, + const MachOObjectFile *O) const; /// For use examining a trie not in a MachOObjectFile. - static iterator_range<export_iterator> exports(ArrayRef<uint8_t> Trie); + static iterator_range<export_iterator> exports(Error &Err, + ArrayRef<uint8_t> Trie, + const MachOObjectFile *O = + nullptr); /// For use iterating over all rebase table entries. iterator_range<rebase_iterator> rebaseTable(Error &Err); diff --git a/llvm/lib/Object/MachOObjectFile.cpp b/llvm/lib/Object/MachOObjectFile.cpp index 2e4da9f15aa..8b85049ea63 100644 --- a/llvm/lib/Object/MachOObjectFile.cpp +++ b/llvm/lib/Object/MachOObjectFile.cpp @@ -2607,10 +2607,14 @@ dice_iterator MachOObjectFile::end_dices() const { return dice_iterator(DiceRef(DRI, this)); } -ExportEntry::ExportEntry(ArrayRef<uint8_t> T) : Trie(T) {} +ExportEntry::ExportEntry(Error *E, const MachOObjectFile *O, + ArrayRef<uint8_t> T) : E(E), O(O), Trie(T) {} void ExportEntry::moveToFirst() { + ErrorAsOutParameter ErrAsOutParam(E); pushNode(0); + if (*E) + return; pushDownUntilBottom(); } @@ -2637,14 +2641,12 @@ bool ExportEntry::operator==(const ExportEntry &Other) const { return true; } -uint64_t ExportEntry::readULEB128(const uint8_t *&Ptr) { +uint64_t ExportEntry::readULEB128(const uint8_t *&Ptr, const char **error) { unsigned Count; - uint64_t Result = decodeULEB128(Ptr, &Count); + uint64_t Result = decodeULEB128(Ptr, &Count, Trie.end(), error); Ptr += Count; - if (Ptr > Trie.end()) { + if (Ptr > Trie.end()) Ptr = Trie.end(); - Malformed = true; - } return Result; } @@ -2679,22 +2681,119 @@ ExportEntry::NodeState::NodeState(const uint8_t *Ptr) : Start(Ptr), Current(Ptr) {} void ExportEntry::pushNode(uint64_t offset) { + ErrorAsOutParameter ErrAsOutParam(E); const uint8_t *Ptr = Trie.begin() + offset; NodeState State(Ptr); - uint64_t ExportInfoSize = readULEB128(State.Current); + const char *error; + uint64_t ExportInfoSize = readULEB128(State.Current, &error); + if (error) { + *E = malformedError("export info size " + Twine(error) + " in export trie " + "data at node: 0x" + utohexstr(offset)); + moveToEnd(); + return; + } State.IsExportNode = (ExportInfoSize != 0); const uint8_t* Children = State.Current + ExportInfoSize; + if (Children > Trie.end()) { + *E = malformedError("export info size: 0x" + utohexstr(ExportInfoSize) + + " in export trie data at node: 0x" + utohexstr(offset) + + " too big and extends past end of trie data"); + moveToEnd(); + return; + } if (State.IsExportNode) { - State.Flags = readULEB128(State.Current); + const uint8_t *ExportStart = State.Current; + State.Flags = readULEB128(State.Current, &error); + if (error) { + *E = malformedError("flags " + Twine(error) + " in export trie data at " + "node: 0x" + utohexstr(offset)); + moveToEnd(); + return; + } + uint64_t Kind = State.Flags & MachO::EXPORT_SYMBOL_FLAGS_KIND_MASK; + if (State.Flags != 0 && + (Kind != MachO::EXPORT_SYMBOL_FLAGS_KIND_REGULAR && + Kind != MachO::EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE && + Kind != MachO::EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL)) { + *E = malformedError("unsupported exported symbol kind: " + + Twine((int)Kind) + " in flags: 0x" + utohexstr(State.Flags) + + " in export trie data at node: 0x" + utohexstr(offset)); + moveToEnd(); + return; + } if (State.Flags & MachO::EXPORT_SYMBOL_FLAGS_REEXPORT) { State.Address = 0; - State.Other = readULEB128(State.Current); // dylib ordinal + State.Other = readULEB128(State.Current, &error); // dylib ordinal + if (error) { + *E = malformedError("dylib ordinal of re-export " + Twine(error) + + " in export trie data at node: 0x" + utohexstr(offset)); + moveToEnd(); + return; + } + if (O != nullptr) { + if (State.Other > O->getLibraryCount()) { + *E = malformedError("bad library ordinal: " + Twine((int)State.Other) + + " (max " + Twine((int)O->getLibraryCount()) + ") in export " + "trie data at node: 0x" + utohexstr(offset)); + moveToEnd(); + return; + } + } State.ImportName = reinterpret_cast<const char*>(State.Current); + if (*State.ImportName == '\0') { + State.Current++; + } else { + const uint8_t *End = State.Current + 1; + if (End >= Trie.end()) { + *E = malformedError("import name of re-export in export trie data at " + "node: 0x" + utohexstr(offset) + " starts past end of trie " + "data"); + moveToEnd(); + return; + } + while(*End != '\0' && End < Trie.end()) + End++; + if (*End != '\0') { + *E = malformedError("import name of re-export in export trie data at " + "node: 0x" + utohexstr(offset) + " extends past end of trie " + "data"); + moveToEnd(); + return; + } + State.Current = End + 1; + } } else { - State.Address = readULEB128(State.Current); - if (State.Flags & MachO::EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) - State.Other = readULEB128(State.Current); + State.Address = readULEB128(State.Current, &error); + if (error) { + *E = malformedError("address " + Twine(error) + " in export trie data " + "at node: 0x" + utohexstr(offset)); + moveToEnd(); + return; + } + if (State.Flags & MachO::EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) { + State.Other = readULEB128(State.Current, &error); + if (error) { + *E = malformedError("resolver of stub and resolver " + Twine(error) + + " in export trie data at node: 0x" + utohexstr(offset)); + moveToEnd(); + return; + } + } } + if(ExportStart + ExportInfoSize != State.Current) { + *E = malformedError("inconsistant export info size: 0x" + + utohexstr(ExportInfoSize) + " where actual size was: 0x" + + utohexstr(State.Current - ExportStart) + " in export trie data " + "at node: 0x" + utohexstr(offset)); + moveToEnd(); + return; + } + } + if (Children + 1 >= Trie.end()) { + *E = malformedError("byte for count of childern in export trie data at " + "node: 0x" + utohexstr(offset) + " extends past end of trie data"); + moveToEnd(); + return; } State.ChildCount = *Children; State.Current = Children + 1; @@ -2704,21 +2803,50 @@ void ExportEntry::pushNode(uint64_t offset) { } void ExportEntry::pushDownUntilBottom() { + ErrorAsOutParameter ErrAsOutParam(E); + const char *error; while (Stack.back().NextChildIndex < Stack.back().ChildCount) { NodeState &Top = Stack.back(); CumulativeString.resize(Top.ParentStringLength); - for (;*Top.Current != 0; Top.Current++) { + for (;*Top.Current != 0 && Top.Current < Trie.end(); Top.Current++) { char C = *Top.Current; CumulativeString.push_back(C); } + if (Top.Current >= Trie.end()) { + *E = malformedError("edge sub-string in export trie data at node: 0x" + + utohexstr(Top.Start - Trie.begin()) + " for child #" + + Twine((int)Top.NextChildIndex) + " extends past end of trie data"); + moveToEnd(); + return; + } Top.Current += 1; - uint64_t childNodeIndex = readULEB128(Top.Current); + uint64_t childNodeIndex = readULEB128(Top.Current, &error); + if (error) { + *E = malformedError("child node offset " + Twine(error) + + " in export trie data at node: 0x" + + utohexstr(Top.Start - Trie.begin())); + moveToEnd(); + return; + } + for (const NodeState &node : nodes()) { + if (node.Start == Trie.begin() + childNodeIndex){ + *E = malformedError("loop in childern in export trie data at node: 0x" + + utohexstr(Top.Start - Trie.begin()) + " back to node: 0x" + + utohexstr(childNodeIndex)); + moveToEnd(); + return; + } + } Top.NextChildIndex += 1; pushNode(childNodeIndex); + if (*E) + return; } if (!Stack.back().IsExportNode) { - Malformed = true; + *E = malformedError("node is not an export node in export trie data at " + "node: 0x" + utohexstr(Stack.back().Start - Trie.begin())); moveToEnd(); + return; } } @@ -2738,8 +2866,10 @@ void ExportEntry::pushDownUntilBottom() { // stack ivar. If there is no more ways down, it pops up one and tries to go // down a sibling path until a childless node is reached. void ExportEntry::moveNext() { - if (Stack.empty() || !Stack.back().IsExportNode) { - Malformed = true; + assert(!Stack.empty() && "ExportEntry::moveNext() with empty node stack"); + if (!Stack.back().IsExportNode) { + *E = malformedError("node is not an export node in export trie data at " + "node: 0x" + utohexstr(Stack.back().Start - Trie.begin())); moveToEnd(); return; } @@ -2764,21 +2894,23 @@ void ExportEntry::moveNext() { } iterator_range<export_iterator> -MachOObjectFile::exports(ArrayRef<uint8_t> Trie) { - ExportEntry Start(Trie); +MachOObjectFile::exports(Error &E, ArrayRef<uint8_t> Trie, + const MachOObjectFile *O) { + ExportEntry Start(&E, O, Trie); if (Trie.empty()) Start.moveToEnd(); else Start.moveToFirst(); - ExportEntry Finish(Trie); + ExportEntry Finish(&E, O, Trie); Finish.moveToEnd(); return make_range(export_iterator(Start), export_iterator(Finish)); } -iterator_range<export_iterator> MachOObjectFile::exports() const { - return exports(getDyldInfoExportsTrie()); +iterator_range<export_iterator> MachOObjectFile::exports(Error &Err, + const MachOObjectFile *O) const { + return exports(Err, getDyldInfoExportsTrie(), O); } MachORebaseEntry::MachORebaseEntry(Error *E, const MachOObjectFile *O, diff --git a/llvm/test/tools/llvm-objdump/Inputs/macho-inconsistant-export b/llvm/test/tools/llvm-objdump/Inputs/macho-inconsistant-export Binary files differnew file mode 100755 index 00000000000..da137800a8d --- /dev/null +++ b/llvm/test/tools/llvm-objdump/Inputs/macho-inconsistant-export diff --git a/llvm/test/tools/llvm-objdump/Inputs/macho-trie-bad-export-info-malformed-uleb128 b/llvm/test/tools/llvm-objdump/Inputs/macho-trie-bad-export-info-malformed-uleb128 Binary files differnew file mode 100755 index 00000000000..57ae7bd6c07 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/Inputs/macho-trie-bad-export-info-malformed-uleb128 diff --git a/llvm/test/tools/llvm-objdump/Inputs/macho-trie-bad-export-info-malformed-uleb128_too_big b/llvm/test/tools/llvm-objdump/Inputs/macho-trie-bad-export-info-malformed-uleb128_too_big Binary files differnew file mode 100755 index 00000000000..06e005d209d --- /dev/null +++ b/llvm/test/tools/llvm-objdump/Inputs/macho-trie-bad-export-info-malformed-uleb128_too_big diff --git a/llvm/test/tools/llvm-objdump/Inputs/macho-trie-bad-kind b/llvm/test/tools/llvm-objdump/Inputs/macho-trie-bad-kind Binary files differnew file mode 100755 index 00000000000..809d0be2820 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/Inputs/macho-trie-bad-kind diff --git a/llvm/test/tools/llvm-objdump/Inputs/macho-trie-bad-library-ordinal b/llvm/test/tools/llvm-objdump/Inputs/macho-trie-bad-library-ordinal Binary files differnew file mode 100755 index 00000000000..cbe8c2c62d7 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/Inputs/macho-trie-bad-library-ordinal diff --git a/llvm/test/tools/llvm-objdump/Inputs/macho-trie-children-count-byte b/llvm/test/tools/llvm-objdump/Inputs/macho-trie-children-count-byte Binary files differnew file mode 100755 index 00000000000..63d389b6586 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/Inputs/macho-trie-children-count-byte diff --git a/llvm/test/tools/llvm-objdump/Inputs/macho-trie-edge-string-end b/llvm/test/tools/llvm-objdump/Inputs/macho-trie-edge-string-end Binary files differnew file mode 100755 index 00000000000..573bf8ea710 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/Inputs/macho-trie-edge-string-end diff --git a/llvm/test/tools/llvm-objdump/Inputs/macho-trie-export-info-size-too-big b/llvm/test/tools/llvm-objdump/Inputs/macho-trie-export-info-size-too-big Binary files differnew file mode 100755 index 00000000000..e41f314355f --- /dev/null +++ b/llvm/test/tools/llvm-objdump/Inputs/macho-trie-export-info-size-too-big diff --git a/llvm/test/tools/llvm-objdump/Inputs/macho-trie-import-name-end b/llvm/test/tools/llvm-objdump/Inputs/macho-trie-import-name-end Binary files differnew file mode 100755 index 00000000000..f1d8f67ade0 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/Inputs/macho-trie-import-name-end diff --git a/llvm/test/tools/llvm-objdump/Inputs/macho-trie-import-name-start b/llvm/test/tools/llvm-objdump/Inputs/macho-trie-import-name-start Binary files differnew file mode 100755 index 00000000000..4f7e93c4fc0 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/Inputs/macho-trie-import-name-start diff --git a/llvm/test/tools/llvm-objdump/Inputs/macho-trie-node-loop b/llvm/test/tools/llvm-objdump/Inputs/macho-trie-node-loop Binary files differnew file mode 100755 index 00000000000..b94dfa2610e --- /dev/null +++ b/llvm/test/tools/llvm-objdump/Inputs/macho-trie-node-loop diff --git a/llvm/test/tools/llvm-objdump/Inputs/macho-trie-not-export-node b/llvm/test/tools/llvm-objdump/Inputs/macho-trie-not-export-node Binary files differnew file mode 100755 index 00000000000..38882762cf0 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/Inputs/macho-trie-not-export-node diff --git a/llvm/test/tools/llvm-objdump/macho-bad-trie.test b/llvm/test/tools/llvm-objdump/macho-bad-trie.test new file mode 100644 index 00000000000..5309123454c --- /dev/null +++ b/llvm/test/tools/llvm-objdump/macho-bad-trie.test @@ -0,0 +1,35 @@ +RUN: not llvm-objdump -macho -exports-trie %p/Inputs/macho-trie-bad-kind 2>&1 | FileCheck -check-prefix BAD_KIND %s +BAD_KIND: macho-trie-bad-kind': truncated or malformed object (unsupported exported symbol kind: 3 in flags: 0x13 in export trie data at node: 0x53) + +RUN: not llvm-objdump -macho -exports-trie %p/Inputs/macho-trie-bad-export-info-malformed-uleb128 2>&1 | FileCheck -check-prefix MALFORMED_ULEB128 %s +MALFORMED_ULEB128: macho-trie-bad-export-info-malformed-uleb128': truncated or malformed object (export info size malformed uleb128, extends past end in export trie data at node: 0x5A) + +RUN: not llvm-objdump -macho -exports-trie %p/Inputs/macho-trie-bad-export-info-malformed-uleb128_too_big 2>&1 | FileCheck -check-prefix MALFORMED_ULEB128_TOO_BIG %s +MALFORMED_ULEB128_TOO_BIG: macho-trie-bad-export-info-malformed-uleb128_too_big': truncated or malformed object (export info size uleb128 too big for uint64 in export trie data at node: 0x5A) + +RUN: not llvm-objdump -macho -exports-trie %p/Inputs/macho-trie-export-info-size-too-big 2>&1 | FileCheck -check-prefix EXPORT_INFO_SIZE_TOO_BIG %s +EXPORT_INFO_SIZE_TOO_BIG: macho-trie-export-info-size-too-big': truncated or malformed object (export info size: 0x1234 in export trie data at node: 0x33 too big and extends past end of trie data) + +RUN: not llvm-objdump -macho -exports-trie %p/Inputs/macho-trie-children-count-byte 2>&1 | FileCheck -check-prefix CHILDREN_COUNT_BYTE %s +CHILDREN_COUNT_BYTE: macho-trie-children-count-byte': truncated or malformed object (byte for count of childern in export trie data at node: 0x5 extends past end of trie data) + +RUN: not llvm-objdump -macho -exports-trie %p/Inputs/macho-trie-import-name-start 2>&1 | FileCheck -check-prefix IMPORT_NAME_START %s +IMPORT_NAME_START: macho-trie-import-name-start': truncated or malformed object (import name of re-export in export trie data at node: 0x33 starts past end of trie data) + +RUN: not llvm-objdump -macho -exports-trie %p/Inputs/macho-trie-import-name-end 2>&1 | FileCheck -check-prefix IMPORT_NAME_END %s +IMPORT_NAME_END: macho-trie-import-name-end': truncated or malformed object (import name of re-export in export trie data at node: 0x33 extends past end of trie data) + +RUN: not llvm-objdump -macho -exports-trie %p/Inputs/macho-trie-edge-string-end 2>&1 | FileCheck -check-prefix EDGE_STRING_END %s +EDGE_STRING_END: macho-trie-edge-string-end': truncated or malformed object (edge sub-string in export trie data at node: 0x42 for child #0 extends past end of trie data) + +RUN: not llvm-objdump -macho -exports-trie %p/Inputs/macho-trie-not-export-node 2>&1 | FileCheck -check-prefix NOT_EXPORT_NODE %s +NOT_EXPORT_NODE: macho-trie-not-export-node': truncated or malformed object (node is not an export node in export trie data at node: 0x5A) + +RUN: not llvm-objdump -macho -exports-trie %p/Inputs/macho-trie-node-loop 2>&1 | FileCheck -check-prefix LOOP_OF_CHILDERN %s +LOOP_OF_CHILDERN: macho-trie-node-loop': truncated or malformed object (loop in childern in export trie data at node: 0x42 back to node: 0x5) + +RUN: not llvm-objdump -macho -exports-trie %p/Inputs/macho-trie-bad-library-ordinal 2>&1 | FileCheck -check-prefix BAD_LIBRARY_ORDINAL %s +BAD_LIBRARY_ORDINAL: macho-trie-bad-library-ordinal': truncated or malformed object (bad library ordinal: 69 (max 3) in export trie data at node: 0x33) + +RUN: not llvm-objdump -macho -exports-trie %p/Inputs/macho-inconsistant-export 2>&1 | FileCheck -check-prefix INCONSISTANT_EXPORT_SIZE %s +INCONSISTANT_EXPORT_SIZE: macho-inconsistant-export': truncated or malformed object (inconsistant export info size: 0x9 where actual size was: 0x5 in export trie data at node: 0x53) diff --git a/llvm/tools/llvm-nm/llvm-nm.cpp b/llvm/tools/llvm-nm/llvm-nm.cpp index ea47891250f..e6378a74306 100644 --- a/llvm/tools/llvm-nm/llvm-nm.cpp +++ b/llvm/tools/llvm-nm/llvm-nm.cpp @@ -1226,7 +1226,9 @@ dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName, if (DyldInfoOnly || AddDyldInfo || HFlags & MachO::MH_NLIST_OUTOFSYNC_WITH_DYLDINFO) { unsigned ExportsAdded = 0; - for (const llvm::object::ExportEntry &Entry : MachO->exports()) { + Error Err = Error::success(); + for (const llvm::object::ExportEntry &Entry : MachO->exports(Err, + MachO)) { bool found = false; bool ReExport = false; if (!DyldInfoOnly) { @@ -1362,6 +1364,8 @@ dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName, } } } + if (Err) + error(std::move(Err), MachO->getFileName()); // Set the symbol names and indirect names for the added symbols. if (ExportsAdded) { EOS.flush(); diff --git a/llvm/tools/llvm-objdump/MachODump.cpp b/llvm/tools/llvm-objdump/MachODump.cpp index 3bab94d681a..31a3f66b1d3 100644 --- a/llvm/tools/llvm-objdump/MachODump.cpp +++ b/llvm/tools/llvm-objdump/MachODump.cpp @@ -9402,7 +9402,8 @@ void llvm::printMachOExportsTrie(const object::MachOObjectFile *Obj) { } } } - for (const llvm::object::ExportEntry &Entry : Obj->exports()) { + Error Err = Error::success(); + for (const llvm::object::ExportEntry &Entry : Obj->exports(Err, Obj)) { uint64_t Flags = Entry.flags(); bool ReExport = (Flags & MachO::EXPORT_SYMBOL_FLAGS_REEXPORT); bool WeakDef = (Flags & MachO::EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION); @@ -9455,6 +9456,8 @@ void llvm::printMachOExportsTrie(const object::MachOObjectFile *Obj) { } outs() << "\n"; } + if (Err) + report_error(Obj->getFileName(), std::move(Err)); } //===----------------------------------------------------------------------===// |