diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDataExtractor.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDataExtractor.h --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDataExtractor.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDataExtractor.h @@ -9,6 +9,7 @@ #ifndef LLVM_DEBUGINFO_DWARFDATAEXTRACTOR_H #define LLVM_DEBUGINFO_DWARFDATAEXTRACTOR_H +#include "llvm/BinaryFormat/Dwarf.h" #include "llvm/DebugInfo/DWARF/DWARFSection.h" #include "llvm/Support/DataExtractor.h" @@ -38,11 +39,22 @@ StringRef(reinterpret_cast(Data.data()), Data.size()), IsLittleEndian, AddressSize) {} + std::pair + getInitialLength(uint64_t *Off, Error *Err = nullptr) const; + + std::pair getInitialLength(Cursor &C) const { + return getInitialLength(&getOffset(C), &getError(C)); + } + /// Extracts a value and applies a relocation to the result if /// one exists for the given offset. uint64_t getRelocatedValue(uint32_t Size, uint64_t *Off, uint64_t *SectionIndex = nullptr, Error *Err = nullptr) const; + uint64_t getRelocatedValue(Cursor &C, uint32_t Size, + uint64_t *SectionIndex = nullptr) const { + return getRelocatedValue(Size, &getOffset(C), SectionIndex, &getError(C)); + } /// Extracts an address-sized value and applies a relocation to the result if /// one exists for the given offset. diff --git a/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp b/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp --- a/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp @@ -377,56 +377,31 @@ Error DWARFDebugNames::Header::extract(const DWARFDataExtractor &AS, uint64_t *Offset) { - uint64_t StartingOffset = *Offset; - // Check that we can read the unit length field. - if (!AS.isValidOffsetForDataOfSize(StartingOffset, 4)) - return createStringError(errc::illegal_byte_sequence, - "Section too small: cannot read header."); - UnitLength = AS.getU32(Offset); - if (UnitLength >= dwarf::DW_LENGTH_lo_reserved && - UnitLength != dwarf::DW_LENGTH_DWARF64) - return createStringError(errc::illegal_byte_sequence, - "Unsupported reserved unit length value"); - Format = (UnitLength == dwarf::DW_LENGTH_DWARF64) ? dwarf::DWARF64 - : dwarf::DWARF32; - - // These fields are the same for 32-bit and 64-bit DWARF formats. - constexpr unsigned CommonHeaderSize = 2 + // Version - 2 + // Padding - 4 + // CU count - 4 + // Local TU count - 4 + // Foreign TU count - 4 + // Bucket count - 4 + // Name count - 4 + // Abbreviations table size - 4; // Augmentation string size - // Check that we can read the fixed-size part. - if (!AS.isValidOffsetForDataOfSize( - StartingOffset, - CommonHeaderSize + dwarf::getUnitLengthFieldByteSize(Format))) - return createStringError(errc::illegal_byte_sequence, - "Section too small: cannot read header."); - if (Format == dwarf::DWARF64) - UnitLength = AS.getU64(Offset); - Version = AS.getU16(Offset); - // Skip padding - *Offset += 2; - CompUnitCount = AS.getU32(Offset); - LocalTypeUnitCount = AS.getU32(Offset); - ForeignTypeUnitCount = AS.getU32(Offset); - BucketCount = AS.getU32(Offset); - NameCount = AS.getU32(Offset); - AbbrevTableSize = AS.getU32(Offset); - AugmentationStringSize = alignTo(AS.getU32(Offset), 4); - - if (!AS.isValidOffsetForDataOfSize(*Offset, AugmentationStringSize)) + DataExtractor::Cursor C(*Offset); + std::tie(UnitLength, Format) = AS.getInitialLength(C); + + Version = AS.getU16(C); + AS.skip(C, 2); // padding + CompUnitCount = AS.getU32(C); + LocalTypeUnitCount = AS.getU32(C); + ForeignTypeUnitCount = AS.getU32(C); + BucketCount = AS.getU32(C); + NameCount = AS.getU32(C); + AbbrevTableSize = AS.getU32(C); + AugmentationStringSize = alignTo(AS.getU32(C), 4); + + if (!C) + return C.takeError(); + + if (!AS.isValidOffsetForDataOfSize(C.tell(), AugmentationStringSize)) return createStringError( errc::illegal_byte_sequence, "Section too small: cannot read header augmentation."); AugmentationString.resize(AugmentationStringSize); - AS.getU8(Offset, reinterpret_cast(AugmentationString.data()), + AS.getU8(C, reinterpret_cast(AugmentationString.data()), AugmentationStringSize); - return Error::success(); + *Offset = C.tell(); + return C.takeError(); } void DWARFDebugNames::Abbrev::dump(ScopedPrinter &W) const { diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDataExtractor.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDataExtractor.cpp --- a/llvm/lib/DebugInfo/DWARF/DWARFDataExtractor.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDataExtractor.cpp @@ -7,11 +7,44 @@ //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h" -#include "llvm/BinaryFormat/Dwarf.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" using namespace llvm; +std::pair +DWARFDataExtractor::getInitialLength(uint64_t *Off, Error *Err) const { + ErrorAsOutParameter ErrAsOut(Err); + if (Err && *Err) + return {0, dwarf::DWARF32}; + + Cursor C(*Off); + uint64_t Length = getRelocatedValue(C, 4); + dwarf::DwarfFormat Format = dwarf::DWARF32; + if (Length == dwarf::DW_LENGTH_DWARF64) { + Length = getRelocatedValue(C, 8); + Format = dwarf::DWARF64; + } else if (Length >= dwarf::DW_LENGTH_lo_reserved) { + cantFail(C.takeError()); + if (Err) + *Err = createStringError( + errc::invalid_argument, + "Unsupported reserved unit length of value 0x%8.8" PRIx64 + " at offset 0x%" PRIx64, + Length, *Off); + return {0, dwarf::DWARF32}; + } + + if (C) { + *Off = C.tell(); + return {Length, Format}; + } + if (Err) + *Err = C.takeError(); + else + consumeError(C.takeError()); + return {0, dwarf::DWARF32}; +} + uint64_t DWARFDataExtractor::getRelocatedValue(uint32_t Size, uint64_t *Off, uint64_t *SecNdx, Error *Err) const { diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp @@ -332,18 +332,12 @@ const uint64_t PrologueOffset = *OffsetPtr; clear(); - TotalLength = DebugLineData.getRelocatedValue(4, OffsetPtr); - if (TotalLength == dwarf::DW_LENGTH_DWARF64) { - FormParams.Format = dwarf::DWARF64; - TotalLength = DebugLineData.getU64(OffsetPtr); - } else if (TotalLength >= dwarf::DW_LENGTH_lo_reserved) { - // Treat this error as unrecoverable - we have no way of knowing where the - // table ends. - return createStringError(errc::invalid_argument, - "parsing line table prologue at offset 0x%8.8" PRIx64 - " unsupported reserved unit length found of value 0x%8.8" PRIx64, - PrologueOffset, TotalLength); - } + Error Err = Error::success(); + std::tie(TotalLength, FormParams.Format) = + DebugLineData.getInitialLength(OffsetPtr, &Err); + if (Err) + return Err; + FormParams.Version = DebugLineData.getU16(OffsetPtr); if (getVersion() < 2 || getVersion() > 5) // Treat this error as unrecoverable - we cannot be sure what any of @@ -1196,8 +1190,7 @@ } bool DWARFDebugLine::Prologue::totalLengthIsValid() const { - return TotalLength == dwarf::DW_LENGTH_DWARF64 || - TotalLength < dwarf::DW_LENGTH_lo_reserved; + return TotalLength != 0u; } DWARFDebugLine::LineTable DWARFDebugLine::SectionParser::parseNext( diff --git a/llvm/lib/DebugInfo/DWARF/DWARFListTable.cpp b/llvm/lib/DebugInfo/DWARF/DWARFListTable.cpp --- a/llvm/lib/DebugInfo/DWARF/DWARFListTable.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFListTable.cpp @@ -18,25 +18,13 @@ Error DWARFListTableHeader::extract(DWARFDataExtractor Data, uint64_t *OffsetPtr) { HeaderOffset = *OffsetPtr; - // Read and verify the length field. - if (!Data.isValidOffsetForDataOfSize(*OffsetPtr, sizeof(uint32_t))) - return createStringError(errc::invalid_argument, - "section is not large enough to contain a " - "%s table length at offset 0x%" PRIx64, - SectionName.data(), *OffsetPtr); - Format = dwarf::DwarfFormat::DWARF32; - uint8_t OffsetByteSize = 4; - HeaderData.Length = Data.getRelocatedValue(4, OffsetPtr); - if (HeaderData.Length == dwarf::DW_LENGTH_DWARF64) { - Format = dwarf::DwarfFormat::DWARF64; - OffsetByteSize = 8; - HeaderData.Length = Data.getU64(OffsetPtr); - } else if (HeaderData.Length >= dwarf::DW_LENGTH_lo_reserved) { - return createStringError(errc::invalid_argument, - "%s table at offset 0x%" PRIx64 - " has unsupported reserved unit length of value 0x%8.8" PRIx64, - SectionName.data(), HeaderOffset, HeaderData.Length); - } + Error Err = Error::success(); + + std::tie(HeaderData.Length, Format) = Data.getInitialLength(OffsetPtr, &Err); + if (Err) + return Err; + + uint8_t OffsetByteSize = Format == dwarf::DWARF64 ? 8 : 4; uint64_t FullLength = HeaderData.Length + dwarf::getUnitLengthFieldByteSize(Format); assert(FullLength == length()); diff --git a/llvm/test/tools/llvm-dwarfdump/X86/debug-names-verify-short1.s b/llvm/test/tools/llvm-dwarfdump/X86/debug-names-verify-short1.s --- a/llvm/test/tools/llvm-dwarfdump/X86/debug-names-verify-short1.s +++ b/llvm/test/tools/llvm-dwarfdump/X86/debug-names-verify-short1.s @@ -1,7 +1,7 @@ # RUN: llvm-mc -triple x86_64-pc-linux %s -filetype=obj | \ # RUN: not llvm-dwarfdump -verify - | FileCheck %s -# CHECK: Section too small: cannot read header. +# CHECK: error: unexpected end of data .section .debug_str,"MS",@progbits,1 .Lstring_producer: diff --git a/llvm/test/tools/llvm-dwarfdump/X86/debug_line_invalid.test b/llvm/test/tools/llvm-dwarfdump/X86/debug_line_invalid.test --- a/llvm/test/tools/llvm-dwarfdump/X86/debug_line_invalid.test +++ b/llvm/test/tools/llvm-dwarfdump/X86/debug_line_invalid.test @@ -50,7 +50,7 @@ ## For fatal issues, the following table(s) should not be dumped: # FATAL: debug_line[0x00000048] # FATAL-NEXT: Line table prologue -# FATAL-NEXT: total_length: 0xfffffffe +# FATAL-NEXT: total_length: 0x00000000 # FATAL-NOT: debug_line ## For non-fatal issues, the table data should be dumped: @@ -192,7 +192,7 @@ # LAST: debug_line[0x000003c9] # LAST: 0x00000000cafebabe {{.*}} end_sequence -# RESERVED: warning: parsing line table prologue at offset 0x00000048 unsupported reserved unit length found of value 0xfffffffe +# RESERVED: warning: Unsupported reserved unit length of value 0xfffffffe at offset 0x48 # ALL-NOT: warning: # ALL: warning: parsing line table prologue at offset 0x00000048 found unsupported version 0 diff --git a/llvm/test/tools/llvm-dwarfdump/X86/debug_rnglists_invalid.s b/llvm/test/tools/llvm-dwarfdump/X86/debug_rnglists_invalid.s --- a/llvm/test/tools/llvm-dwarfdump/X86/debug_rnglists_invalid.s +++ b/llvm/test/tools/llvm-dwarfdump/X86/debug_rnglists_invalid.s @@ -2,7 +2,7 @@ # RUN: llvm-dwarfdump --debug-rnglists - 2>&1 | FileCheck %s --check-prefix=SHORT # SHORT-NOT: error: # SHORT-NOT: range list header -# SHORT: error: section is not large enough to contain a .debug_rnglists table length at offset 0 +# SHORT: error: unexpected end of data # SHORT-NOT: range list header # SHORT-NOT: error: diff --git a/llvm/test/tools/llvm-dwarfdump/X86/debug_rnglists_reserved_length.s b/llvm/test/tools/llvm-dwarfdump/X86/debug_rnglists_reserved_length.s --- a/llvm/test/tools/llvm-dwarfdump/X86/debug_rnglists_reserved_length.s +++ b/llvm/test/tools/llvm-dwarfdump/X86/debug_rnglists_reserved_length.s @@ -2,7 +2,7 @@ # RUN: llvm-dwarfdump --debug-rnglists - 2>&1 | \ # RUN: FileCheck %s --implicit-check-not=error -# CHECK: error: .debug_rnglists table at offset 0x0 has unsupported reserved unit length of value 0xfffffff0 +# CHECK: error: Unsupported reserved unit length of value 0xfffffff0 at offset 0x0 .section .debug_rnglists,"",@progbits .long 0xfffffff0 diff --git a/llvm/unittests/DebugInfo/DWARF/CMakeLists.txt b/llvm/unittests/DebugInfo/DWARF/CMakeLists.txt --- a/llvm/unittests/DebugInfo/DWARF/CMakeLists.txt +++ b/llvm/unittests/DebugInfo/DWARF/CMakeLists.txt @@ -12,6 +12,7 @@ DwarfGenerator.cpp DwarfUtils.cpp DWARFAcceleratorTableTest.cpp + DWARFDataExtractorTest.cpp DWARFDebugArangeSetTest.cpp DWARFDebugInfoTest.cpp DWARFDebugLineTest.cpp diff --git a/llvm/unittests/DebugInfo/DWARF/DWARFAcceleratorTableTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFAcceleratorTableTest.cpp --- a/llvm/unittests/DebugInfo/DWARF/DWARFAcceleratorTableTest.cpp +++ b/llvm/unittests/DebugInfo/DWARF/DWARFAcceleratorTableTest.cpp @@ -16,12 +16,7 @@ void ExpectDebugNamesExtractError(StringRef NamesSecData, StringRef StrSecData, const char *ErrorMessage) { - DWARFSection NamesDWARFSection; - NamesDWARFSection.Data = NamesSecData; - StringMap> Sections; - auto Context = DWARFContext::create(Sections, /* AddrSize = */ 4, - /* isLittleEndian = */ true); - DWARFDataExtractor NamesExtractor(Context->getDWARFObj(), NamesDWARFSection, + DWARFDataExtractor NamesExtractor(NamesSecData, /* isLittleEndian = */ true, /* AddrSize = */ 4); DataExtractor StrExtractor(StrSecData, @@ -36,17 +31,16 @@ TEST(DWARFDebugNames, ReservedUnitLength) { static const char NamesSecData[64] = "\xf0\xff\xff\xff"; // Reserved unit length value - ExpectDebugNamesExtractError(StringRef(NamesSecData, sizeof(NamesSecData)), - StringRef(), - "Unsupported reserved unit length value"); + ExpectDebugNamesExtractError( + StringRef(NamesSecData, sizeof(NamesSecData)), StringRef(), + "Unsupported reserved unit length of value 0xfffffff0 at offset 0x0"); } TEST(DWARFDebugNames, TooSmallForDWARF64) { // DWARF64 header takes at least 44 bytes. static const char NamesSecData[43] = "\xff\xff\xff\xff"; // DWARF64 mark - ExpectDebugNamesExtractError( - StringRef(NamesSecData, sizeof(NamesSecData)), StringRef(), - "Section too small: cannot read header."); + ExpectDebugNamesExtractError(StringRef(NamesSecData, sizeof(NamesSecData)), + StringRef(), "unexpected end of data"); } } // end anonymous namespace diff --git a/llvm/unittests/DebugInfo/DWARF/DWARFDataExtractorTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFDataExtractorTest.cpp new file mode 100644 --- /dev/null +++ b/llvm/unittests/DebugInfo/DWARF/DWARFDataExtractorTest.cpp @@ -0,0 +1,80 @@ +//===- DWARFDataExtractorTest.cpp -----------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h" +#include "llvm/Testing/Support/Error.h" +#include "gtest/gtest.h" +using namespace llvm; + +namespace { + +TEST(DWARFDataExtractorTest, getInitialLength) { + auto GetWithError = [](ArrayRef Bytes) + -> Expected> { + DWARFDataExtractor Data(Bytes, /*IsLittleEndian=*/false, /*AddressSize=*/8); + DWARFDataExtractor::Cursor C(0); + uint64_t Length; + dwarf::DwarfFormat Format; + std::tie(Length, Format) = Data.getInitialLength(C); + if (C) + return std::make_tuple(Length, Format, C.tell()); + + EXPECT_EQ(Length, 0u); + EXPECT_EQ(Format, dwarf::DWARF32); + EXPECT_EQ(C.tell(), 0u); + return C.takeError(); + }; + auto GetWithoutError = [](ArrayRef Bytes) { + DWARFDataExtractor Data(Bytes, /*IsLittleEndian=*/false, /*AddressSize=*/8); + uint64_t Offset = 0; + uint64_t Length; + dwarf::DwarfFormat Format; + std::tie(Length, Format) = Data.getInitialLength(&Offset); + return std::make_tuple(Length, Format, Offset); + }; + auto ErrorResult = std::make_tuple(0, dwarf::DWARF32, 0); + + EXPECT_THAT_EXPECTED(GetWithError({}), Failed()); + EXPECT_EQ(GetWithoutError({}), ErrorResult); + + EXPECT_THAT_EXPECTED(GetWithError({0x00, 0x01, 0x02}), Failed()); + EXPECT_EQ(GetWithoutError({0x00, 0x01, 0x02}), ErrorResult); + + EXPECT_THAT_EXPECTED( + GetWithError({0x00, 0x01, 0x02, 0x03}), + HasValue(std::make_tuple(0x00010203, dwarf::DWARF32, 4))); + EXPECT_EQ(GetWithoutError({0x00, 0x01, 0x02, 0x03}), + std::make_tuple(0x00010203, dwarf::DWARF32, 4)); + + EXPECT_THAT_EXPECTED( + GetWithError({0x00, 0x00, 0x00, 0x00}), + HasValue(std::make_tuple(0x00000000, dwarf::DWARF32, 4))); + EXPECT_EQ(GetWithoutError({0x00, 0x00, 0x00, 0x00}), + std::make_tuple(0x00000000, dwarf::DWARF32, 4)); + + EXPECT_THAT_EXPECTED(GetWithError({0xff, 0xff, 0xff, 0xf0}), Failed()); + EXPECT_EQ(GetWithoutError({0xff, 0xff, 0xff, 0xf0}), ErrorResult); + + EXPECT_THAT_EXPECTED(GetWithError({0xff, 0xff, 0xff, 0xff}), Failed()); + EXPECT_EQ(GetWithoutError({0xff, 0xff, 0xff, 0xff}), ErrorResult); + + EXPECT_THAT_EXPECTED(GetWithError({0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03}), + Failed()); + EXPECT_EQ(GetWithoutError({0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03}), + ErrorResult); + + EXPECT_THAT_EXPECTED( + GetWithError({0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07}), + HasValue(std::make_tuple(0x0001020304050607, dwarf::DWARF64, 12))); + EXPECT_EQ(GetWithoutError({0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, + 0x04, 0x05, 0x06, 0x07}), + std::make_tuple(0x0001020304050607, dwarf::DWARF64, 12)); +} + +} diff --git a/llvm/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp --- a/llvm/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp +++ b/llvm/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp @@ -335,8 +335,7 @@ generate(); checkGetOrParseLineTableEmitsFatalError( - "parsing line table prologue at offset 0x00000000 unsupported reserved " - "unit length found of value 0xfffffff0"); + "Unsupported reserved unit length of value 0xfffffff0 at offset 0x0"); } struct DebugLineUnsupportedVersionFixture : public TestWithParam, @@ -739,13 +738,13 @@ DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs); Parser.parseNext(RecordRecoverable, RecordUnrecoverable); - EXPECT_EQ(Parser.getOffset(), 4u); + EXPECT_EQ(Parser.getOffset(), 0u); EXPECT_TRUE(Parser.done()); EXPECT_FALSE(Recoverable); - checkError("parsing line table prologue at offset 0x00000000 unsupported " - "reserved unit length found of value 0xfffffff0", - std::move(Unrecoverable)); + checkError( + "Unsupported reserved unit length of value 0xfffffff0 at offset 0x0", + std::move(Unrecoverable)); } TEST_F(DebugLineBasicFixture, ParserMovesToEndForBadLengthWhenSkipping) { @@ -760,13 +759,13 @@ DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs); Parser.skip(RecordRecoverable, RecordUnrecoverable); - EXPECT_EQ(Parser.getOffset(), 4u); + EXPECT_EQ(Parser.getOffset(), 0u); EXPECT_TRUE(Parser.done()); EXPECT_FALSE(Recoverable); - checkError("parsing line table prologue at offset 0x00000000 unsupported " - "reserved unit length found of value 0xfffffff0", - std::move(Unrecoverable)); + checkError( + "Unsupported reserved unit length of value 0xfffffff0 at offset 0x0", + std::move(Unrecoverable)); } TEST_F(DebugLineBasicFixture, ParserReportsFirstErrorInEachTableWhenParsing) {