From f864e512def168fb0dc907c76911f8a8a50bf9d2 Mon Sep 17 00:00:00 2001 From: Tim Northover Date: Tue, 23 Jan 2018 13:51:57 +0000 Subject: [PATCH] llvm-objdump: prevent out of bounds accesses during unwind dumping. We were a bit too trusting about the offsets encoded in MachO compact unwind sections, so this passes every access through a bounds check just in case. It prevents a few segfaults on malformed object files, if one should ever come along. Mostly to silence fuzzers in the vague hope they might be able to produce something useful without the noise. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@323198 91177308-0d34-0410-b5e6-96231b3b80d8 --- tools/llvm-objdump/MachODump.cpp | 137 +++++++++++++++++++++++---------------- 1 file changed, 80 insertions(+), 57 deletions(-) diff --git a/tools/llvm-objdump/MachODump.cpp b/tools/llvm-objdump/MachODump.cpp index 9908c2f2d01..aa6122b05f8 100644 --- a/tools/llvm-objdump/MachODump.cpp +++ b/tools/llvm-objdump/MachODump.cpp @@ -7310,12 +7310,25 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, namespace { -template static uint64_t readNext(const char *&Buf) { +template +static uint64_t read(StringRef Contents, ptrdiff_t Offset) { using llvm::support::little; using llvm::support::unaligned; - uint64_t Val = support::endian::read(Buf); - Buf += sizeof(T); + if (Offset + sizeof(T) > Contents.size()) { + outs() << "warning: attempt to read past end of buffer\n"; + return T(); + } + + uint64_t Val = + support::endian::read(Contents.data() + Offset); + return Val; +} + +template +static uint64_t readNext(StringRef Contents, ptrdiff_t &Offset) { + T Val = read(Contents, Offset); + Offset += sizeof(T); return Val; } @@ -7335,18 +7348,18 @@ struct CompactUnwindEntry { CompactUnwindEntry(StringRef Contents, unsigned Offset, bool Is64) : OffsetInSection(Offset) { if (Is64) - read(Contents.data() + Offset); + read(Contents, Offset); else - read(Contents.data() + Offset); + read(Contents, Offset); } private: - template void read(const char *Buf) { - FunctionAddr = readNext(Buf); - Length = readNext(Buf); - CompactEncoding = readNext(Buf); - PersonalityAddr = readNext(Buf); - LSDAAddr = readNext(Buf); + template void read(StringRef Contents, ptrdiff_t Offset) { + FunctionAddr = readNext(Contents, Offset); + Length = readNext(Contents, Offset); + CompactEncoding = readNext(Contents, Offset); + PersonalityAddr = readNext(Contents, Offset); + LSDAAddr = readNext(Contents, Offset); } }; } @@ -7448,7 +7461,7 @@ printMachOCompactUnwindSection(const MachOObjectFile *Obj, // First populate the initial raw offsets, encodings and so on from the entry. for (unsigned Offset = 0; Offset < Contents.size(); Offset += EntrySize) { - CompactUnwindEntry Entry(Contents.data(), Offset, Is64); + CompactUnwindEntry Entry(Contents, Offset, Is64); CompactUnwinds.push_back(Entry); } @@ -7515,19 +7528,19 @@ printMachOCompactUnwindSection(const MachOObjectFile *Obj, // __unwind_info section dumping //===----------------------------------------------------------------------===// -static void printRegularSecondLevelUnwindPage(const char *PageStart) { - const char *Pos = PageStart; - uint32_t Kind = readNext(Pos); +static void printRegularSecondLevelUnwindPage(StringRef PageData) { + ptrdiff_t Pos = 0; + uint32_t Kind = readNext(PageData, Pos); (void)Kind; assert(Kind == 2 && "kind for a regular 2nd level index should be 2"); - uint16_t EntriesStart = readNext(Pos); - uint16_t NumEntries = readNext(Pos); + uint16_t EntriesStart = readNext(PageData, Pos); + uint16_t NumEntries = readNext(PageData, Pos); - Pos = PageStart + EntriesStart; + Pos = EntriesStart; for (unsigned i = 0; i < NumEntries; ++i) { - uint32_t FunctionOffset = readNext(Pos); - uint32_t Encoding = readNext(Pos); + uint32_t FunctionOffset = readNext(PageData, Pos); + uint32_t Encoding = readNext(PageData, Pos); outs() << " [" << i << "]: " << "function offset=" << format("0x%08" PRIx32, FunctionOffset) @@ -7537,24 +7550,23 @@ static void printRegularSecondLevelUnwindPage(const char *PageStart) { } static void printCompressedSecondLevelUnwindPage( - const char *PageStart, uint32_t FunctionBase, + StringRef PageData, uint32_t FunctionBase, const SmallVectorImpl &CommonEncodings) { - const char *Pos = PageStart; - uint32_t Kind = readNext(Pos); + ptrdiff_t Pos = 0; + uint32_t Kind = readNext(PageData, Pos); (void)Kind; assert(Kind == 3 && "kind for a compressed 2nd level index should be 3"); - uint16_t EntriesStart = readNext(Pos); - uint16_t NumEntries = readNext(Pos); + uint16_t EntriesStart = readNext(PageData, Pos); + uint16_t NumEntries = readNext(PageData, Pos); - uint16_t EncodingsStart = readNext(Pos); - readNext(Pos); - const auto *PageEncodings = reinterpret_cast( - PageStart + EncodingsStart); + uint16_t EncodingsStart = readNext(PageData, Pos); + readNext(PageData, Pos); + StringRef PageEncodings = PageData.substr(EncodingsStart, StringRef::npos); - Pos = PageStart + EntriesStart; + Pos = EntriesStart; for (unsigned i = 0; i < NumEntries; ++i) { - uint32_t Entry = readNext(Pos); + uint32_t Entry = readNext(PageData, Pos); uint32_t FunctionOffset = FunctionBase + (Entry & 0xffffff); uint32_t EncodingIdx = Entry >> 24; @@ -7562,7 +7574,9 @@ static void printCompressedSecondLevelUnwindPage( if (EncodingIdx < CommonEncodings.size()) Encoding = CommonEncodings[EncodingIdx]; else - Encoding = PageEncodings[EncodingIdx - CommonEncodings.size()]; + Encoding = read(PageEncodings, + sizeof(uint32_t) * + (EncodingIdx - CommonEncodings.size())); outs() << " [" << i << "]: " << "function offset=" << format("0x%08" PRIx32, FunctionOffset) @@ -7585,13 +7599,13 @@ static void printMachOUnwindInfoSection(const MachOObjectFile *Obj, StringRef Contents; UnwindInfo.getContents(Contents); - const char *Pos = Contents.data(); + ptrdiff_t Pos = 0; //===---------------------------------- // Section header //===---------------------------------- - uint32_t Version = readNext(Pos); + uint32_t Version = readNext(Contents, Pos); outs() << " Version: " << format("0x%" PRIx32, Version) << '\n'; if (Version != 1) { @@ -7599,24 +7613,24 @@ static void printMachOUnwindInfoSection(const MachOObjectFile *Obj, return; } - uint32_t CommonEncodingsStart = readNext(Pos); + uint32_t CommonEncodingsStart = readNext(Contents, Pos); outs() << " Common encodings array section offset: " << format("0x%" PRIx32, CommonEncodingsStart) << '\n'; - uint32_t NumCommonEncodings = readNext(Pos); + uint32_t NumCommonEncodings = readNext(Contents, Pos); outs() << " Number of common encodings in array: " << format("0x%" PRIx32, NumCommonEncodings) << '\n'; - uint32_t PersonalitiesStart = readNext(Pos); + uint32_t PersonalitiesStart = readNext(Contents, Pos); outs() << " Personality function array section offset: " << format("0x%" PRIx32, PersonalitiesStart) << '\n'; - uint32_t NumPersonalities = readNext(Pos); + uint32_t NumPersonalities = readNext(Contents, Pos); outs() << " Number of personality functions in array: " << format("0x%" PRIx32, NumPersonalities) << '\n'; - uint32_t IndicesStart = readNext(Pos); + uint32_t IndicesStart = readNext(Contents, Pos); outs() << " Index array section offset: " << format("0x%" PRIx32, IndicesStart) << '\n'; - uint32_t NumIndices = readNext(Pos); + uint32_t NumIndices = readNext(Contents, Pos); outs() << " Number of indices in array: " << format("0x%" PRIx32, NumIndices) << '\n'; @@ -7631,9 +7645,9 @@ static void printMachOUnwindInfoSection(const MachOObjectFile *Obj, SmallVector CommonEncodings; outs() << " Common encodings: (count = " << NumCommonEncodings << ")\n"; - Pos = Contents.data() + CommonEncodingsStart; + Pos = CommonEncodingsStart; for (unsigned i = 0; i < NumCommonEncodings; ++i) { - uint32_t Encoding = readNext(Pos); + uint32_t Encoding = readNext(Contents, Pos); CommonEncodings.push_back(Encoding); outs() << " encoding[" << i << "]: " << format("0x%08" PRIx32, Encoding) @@ -7648,9 +7662,9 @@ static void printMachOUnwindInfoSection(const MachOObjectFile *Obj, // roughly). Particularly since they only get 2 bits in the compact encoding. outs() << " Personality functions: (count = " << NumPersonalities << ")\n"; - Pos = Contents.data() + PersonalitiesStart; + Pos = PersonalitiesStart; for (unsigned i = 0; i < NumPersonalities; ++i) { - uint32_t PersonalityFn = readNext(Pos); + uint32_t PersonalityFn = readNext(Contents, Pos); outs() << " personality[" << i + 1 << "]: " << format("0x%08" PRIx32, PersonalityFn) << '\n'; } @@ -7671,13 +7685,13 @@ static void printMachOUnwindInfoSection(const MachOObjectFile *Obj, SmallVector IndexEntries; outs() << " Top level indices: (count = " << NumIndices << ")\n"; - Pos = Contents.data() + IndicesStart; + Pos = IndicesStart; for (unsigned i = 0; i < NumIndices; ++i) { IndexEntry Entry; - Entry.FunctionOffset = readNext(Pos); - Entry.SecondLevelPageStart = readNext(Pos); - Entry.LSDAStart = readNext(Pos); + Entry.FunctionOffset = readNext(Contents, Pos); + Entry.SecondLevelPageStart = readNext(Contents, Pos); + Entry.LSDAStart = readNext(Contents, Pos); IndexEntries.push_back(Entry); outs() << " [" << i << "]: " @@ -7696,12 +7710,14 @@ static void printMachOUnwindInfoSection(const MachOObjectFile *Obj, // the first top-level index's LSDAOffset to the last (sentinel). outs() << " LSDA descriptors:\n"; - Pos = Contents.data() + IndexEntries[0].LSDAStart; - int NumLSDAs = (IndexEntries.back().LSDAStart - IndexEntries[0].LSDAStart) / - (2 * sizeof(uint32_t)); + Pos = IndexEntries[0].LSDAStart; + const uint32_t LSDASize = 2 * sizeof(uint32_t); + int NumLSDAs = + (IndexEntries.back().LSDAStart - IndexEntries[0].LSDAStart) / LSDASize; + for (int i = 0; i < NumLSDAs; ++i) { - uint32_t FunctionOffset = readNext(Pos); - uint32_t LSDAOffset = readNext(Pos); + uint32_t FunctionOffset = readNext(Contents, Pos); + uint32_t LSDAOffset = readNext(Contents, Pos); outs() << " [" << i << "]: " << "function offset=" << format("0x%08" PRIx32, FunctionOffset) << ", " @@ -7729,12 +7745,19 @@ static void printMachOUnwindInfoSection(const MachOObjectFile *Obj, << "base function offset=" << format("0x%08" PRIx32, IndexEntries[i].FunctionOffset) << '\n'; - Pos = Contents.data() + IndexEntries[i].SecondLevelPageStart; - uint32_t Kind = *reinterpret_cast(Pos); + Pos = IndexEntries[i].SecondLevelPageStart; + if (Pos + sizeof(uint32_t) > Contents.size()) { + outs() << "warning: invalid offset for second level page: " << Pos << '\n'; + continue; + } + + uint32_t Kind = + *reinterpret_cast(Contents.data() + Pos); if (Kind == 2) - printRegularSecondLevelUnwindPage(Pos); + printRegularSecondLevelUnwindPage(Contents.substr(Pos, 4096)); else if (Kind == 3) - printCompressedSecondLevelUnwindPage(Pos, IndexEntries[i].FunctionOffset, + printCompressedSecondLevelUnwindPage(Contents.substr(Pos, 4096), + IndexEntries[i].FunctionOffset, CommonEncodings); else outs() << " Skipping 2nd level page with unknown kind " << Kind -- 2.11.0