[llvm] 5e74b2e - llvm-dwarfdump --verify: Add support for .debug_str_offsets[.dwo]

David Blaikie via llvm-commits llvm-commits at lists.llvm.org
Mon Jun 5 12:59:44 PDT 2023


Author: David Blaikie
Date: 2023-06-05T19:59:37Z
New Revision: 5e74b2e8bb9b191aa5ed433820860b30ca7d9baa

URL: https://github.com/llvm/llvm-project/commit/5e74b2e8bb9b191aa5ed433820860b30ca7d9baa
DIFF: https://github.com/llvm/llvm-project/commit/5e74b2e8bb9b191aa5ed433820860b30ca7d9baa.diff

LOG: llvm-dwarfdump --verify: Add support for .debug_str_offsets[.dwo]

Had a couple of issues lately causing corrupted strings due to
problematic str_offsets (overflow due to >4GB .debug_str.dwo section in
a dwp and the dwp tool silently overflowing the 32 bit offsets updated
in the .debug_str_offsets.dwo section, and then more recently two CUs in
a dwo caused the dwp tool to reapply the offset adjustment twice
corrupting str_offsets.dwo as well) - so let's check that the offsets
are valid.

This assumes no suffix merging - if anyone implements that, then this
checking should just be removed for the most part (we could still check
the offsets are within the bounds of .debug_str[.dwo], but nothing more
- any offset in the range would be valid, the offsets wouldn't have to
land at the start of a string)

Added: 
    llvm/test/tools/llvm-dwarfdump/X86/verify_invalid_str_offsets.yaml

Modified: 
    llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h
    llvm/lib/DebugInfo/DWARF/DWARFContext.cpp
    llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp
    llvm/test/tools/llvm-dwarfdump/X86/verify_dwarf5_debug_line.yaml

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h
index b9ead366cb232..ac890cdf065f8 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h
@@ -338,6 +338,17 @@ class DWARFVerifier {
   /// \returns true if the existing Apple-style accelerator tables verify
   /// successfully, false otherwise.
   bool handleAccelTables();
+
+  /// Verify the information in the .debug_str_offsets[.dwo].
+  ///
+  /// Any errors are reported to the stream that was this object was
+  /// constructed with.
+  ///
+  /// \returns true if the .debug_line verifies successfully, false otherwise.
+  bool handleDebugStrOffsets();
+  bool verifyDebugStrOffsets(
+      StringRef SectionName, const DWARFSection &Section, StringRef StrData,
+      void (DWARFObject::*)(function_ref<void(const DWARFSection &)>) const);
 };
 
 static inline bool operator<(const DWARFVerifier::DieRangeInfo &LHS,

diff  --git a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp
index 8033ab4d9573a..d9bc78a410d5d 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp
@@ -776,6 +776,8 @@ bool DWARFContext::verify(raw_ostream &OS, DIDumpOptions DumpOpts) {
     Success &= verifier.handleDebugInfo();
   if (DumpOpts.DumpType & DIDT_DebugLine)
     Success &= verifier.handleDebugLine();
+  if (DumpOpts.DumpType & DIDT_DebugStrOffsets)
+    Success &= verifier.handleDebugStrOffsets();
   Success &= verifier.handleAccelTables();
   return Success;
 }

diff  --git a/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp b/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp
index 797ae5484ebef..e0f2fdd718b3a 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp
@@ -1629,6 +1629,116 @@ bool DWARFVerifier::handleAccelTables() {
   return NumErrors == 0;
 }
 
+bool DWARFVerifier::handleDebugStrOffsets() {
+  OS << "Verifying .debug_str_offsets...\n";
+  const DWARFObject &DObj = DCtx.getDWARFObj();
+  bool Success = true;
+  Success &= verifyDebugStrOffsets(
+      ".debug_str_offsets.dwo", DObj.getStrOffsetsDWOSection(),
+      DObj.getStrDWOSection(), &DWARFObject::forEachInfoDWOSections);
+  Success &= verifyDebugStrOffsets(
+      ".debug_str_offsets", DObj.getStrOffsetsSection(), DObj.getStrSection(),
+      &DWARFObject::forEachInfoSections);
+  return Success;
+}
+
+bool DWARFVerifier::verifyDebugStrOffsets(
+    StringRef SectionName, const DWARFSection &Section, StringRef StrData,
+    void (DWARFObject::*VisitInfoSections)(
+        function_ref<void(const DWARFSection &)>) const) {
+  const DWARFObject &DObj = DCtx.getDWARFObj();
+  uint16_t InfoVersion = 0;
+  DwarfFormat InfoFormat = DwarfFormat::DWARF32;
+  (DObj.*VisitInfoSections)([&](const DWARFSection &S) {
+    if (InfoVersion)
+      return;
+    DWARFDataExtractor DebugInfoData(DObj, S, DCtx.isLittleEndian(), 0);
+    uint64_t Offset = 0;
+    InfoFormat = DebugInfoData.getInitialLength(&Offset).second;
+    InfoVersion = DebugInfoData.getU16(&Offset);
+  });
+
+  DWARFDataExtractor DA(DObj, Section, DCtx.isLittleEndian(), 0);
+
+  DataExtractor::Cursor C(0);
+  uint64_t NextUnit = 0;
+  bool Success = true;
+  while (C.seek(NextUnit), C.tell() < DA.getData().size()) {
+    DwarfFormat Format;
+    uint64_t Length;
+    uint64_t StartOffset = C.tell();
+    if (InfoVersion == 4) {
+      Format = InfoFormat;
+      Length = DA.getData().size();
+      NextUnit = C.tell() + Length;
+    } else {
+      std::tie(Length, Format) = DA.getInitialLength(C);
+      if (!C)
+        break;
+      if (C.tell() + Length > DA.getData().size()) {
+        error() << formatv(
+            "{0}: contribution {1:X}: length exceeds available space "
+            "(contribution "
+            "offset ({1:X}) + length field space ({2:X}) + length ({3:X}) == "
+            "{4:X} > section size {5:X})\n",
+            SectionName, StartOffset, C.tell() - StartOffset, Length,
+            C.tell() + Length, DA.getData().size());
+        Success = false;
+        // Nothing more to do - no other contributions to try.
+        break;
+      }
+      NextUnit = C.tell() + Length;
+      uint8_t Version = DA.getU16(C);
+      if (C && Version != 5) {
+        error() << formatv("{0}: contribution {1:X}: invalid version {2}\n",
+                           SectionName, StartOffset, Version);
+        Success = false;
+        // Can't parse the rest of this contribution, since we don't know the
+        // version, but we can pick up with the next contribution.
+        continue;
+      }
+      (void)DA.getU16(C); // padding
+    }
+    uint64_t OffsetByteSize = getDwarfOffsetByteSize(Format);
+    DA.setAddressSize(OffsetByteSize);
+    uint64_t Remainder = (Length - 4) % OffsetByteSize;
+    if (Remainder != 0) {
+      error() << formatv(
+          "{0}: contribution {1:X}: invalid length ((length ({2:X}) "
+          "- header (0x4)) % offset size {3:X} == {4:X} != 0)\n",
+          SectionName, StartOffset, Length, OffsetByteSize, Remainder);
+      Success = false;
+    }
+    for (uint64_t Index = 0; C && C.tell() + OffsetByteSize <= NextUnit; ++Index) {
+      uint64_t OffOff = C.tell();
+      uint64_t StrOff = DA.getAddress(C);
+      // check StrOff refers to the start of a string
+      if (StrOff == 0)
+        continue;
+      if (StrData.size() <= StrOff) {
+        error() << formatv(
+            "{0}: contribution {1:X}: index {2:X}: invalid string "
+            "offset *{3:X} == {4:X}, is beyond the bounds of the string section of length {5:X}\n",
+            SectionName, StartOffset, Index, OffOff, StrOff, StrData.size());
+        continue;
+      }
+      if (StrData[StrOff - 1] == '\0')
+        continue;
+      error() << formatv("{0}: contribution {1:X}: index {2:X}: invalid string "
+                         "offset *{3:X} == {4:X}, is neither zero nor "
+                         "immediately following a null character\n",
+                         SectionName, StartOffset, Index, OffOff, StrOff);
+      Success = false;
+    }
+  }
+
+  if (Error E = C.takeError()) {
+    error() << SectionName << ": " << toString(std::move(E)) << '\n';
+    return false;
+  }
+  return Success;
+}
+
 raw_ostream &DWARFVerifier::error() const { return WithColor::error(OS); }
 
 raw_ostream &DWARFVerifier::warn() const { return WithColor::warning(OS); }

diff  --git a/llvm/test/tools/llvm-dwarfdump/X86/verify_dwarf5_debug_line.yaml b/llvm/test/tools/llvm-dwarfdump/X86/verify_dwarf5_debug_line.yaml
index fc8771da5f532..15de811a31e6c 100644
--- a/llvm/test/tools/llvm-dwarfdump/X86/verify_dwarf5_debug_line.yaml
+++ b/llvm/test/tools/llvm-dwarfdump/X86/verify_dwarf5_debug_line.yaml
@@ -41,8 +41,13 @@ main:                                   # @main
 .Linfo_string2:
 	.asciz	"/tmp"                          # string offset=110
 	.section	.debug_str_offsets,"", at progbits
+        .long   .Lstr_off_end - .Lstr_off_begin # Length of String Offsets Set
+.Lstr_off_begin:
+        .short  5
+        .short  0
 	.long	.Linfo_string0
 	.long	.Linfo_string1
 	.long	.Linfo_string2
+.Lstr_off_end:
 	.ident	"clang version 17.0.0"
 .Lline_table_start0:

diff  --git a/llvm/test/tools/llvm-dwarfdump/X86/verify_invalid_str_offsets.yaml b/llvm/test/tools/llvm-dwarfdump/X86/verify_invalid_str_offsets.yaml
new file mode 100644
index 0000000000000..37f376352a19a
--- /dev/null
+++ b/llvm/test/tools/llvm-dwarfdump/X86/verify_invalid_str_offsets.yaml
@@ -0,0 +1,59 @@
+# RUN: rm -rf %t && split-file %s %t
+# RUN: yaml2obj %t/v5.yaml -o %t/v5.o
+# RUN: not llvm-dwarfdump -debug-str-offsets -verify %t/v5.o | FileCheck %s
+# RUN: yaml2obj %t/v4.yaml -o %t/v4.o
+# RUN: not llvm-dwarfdump -debug-str-offsets -verify %t/v4.o | FileCheck --check-prefix=V4 %s
+
+#      CHECK: Verifying .debug_abbrev...
+#      CHECK: Verifying .debug_str_offsets...
+# CHECK-NEXT: error: .debug_str_offsets: contribution 0x0: index 0x2: invalid string offset *0x10 == 0x1, is neither zero nor immediately following a null character
+# CHECK-NEXT: error: .debug_str_offsets: contribution 0x0: index 0x3: invalid string offset *0x14 == 0x42, is beyond the bounds of the string section of length 0x8
+# CHECK-NEXT: error: .debug_str_offsets: contribution 0x18: invalid version 42
+# CHECK-NEXT: error: .debug_str_offsets: contribution 0x20: invalid length ((length (0x5) - header (0x4)) % offset size 0x4 == 0x1 != 0)
+# CHECK-NEXT: error: .debug_str_offsets: contribution 0x29: length exceeds available space (contribution offset (0x29) + length field space (0x4) + length (0x5000000) == 0x500002D > section size 0x30)
+# Errors detected.
+
+# V4: error: .debug_str_offsets: contribution 0x0: index 0x2: invalid string offset *0x8 == 0x2, is neither zero nor immediately following a null character
+
+
+#--- v4.yaml
+--- !ELF
+FileHeader:
+  Class: ELFCLASS64
+  Data:  ELFDATA2LSB
+  Type:  ET_EXEC
+DWARF:
+  debug_str:
+    - 'foo'
+    - 'bar'
+  debug_info:
+    - Version: 4
+      AddrSize: 4
+Sections:
+  - Name: '.debug_str_offsets'
+    Type: SHT_PROGBITS
+    Content: "000000000400000002000000"
+
+#--- v5.yaml
+--- !ELF
+FileHeader:
+  Class: ELFCLASS64
+  Data:  ELFDATA2LSB
+  Type:  ET_EXEC
+DWARF:
+  debug_str:
+    - 'foo'
+    - 'bar'
+  debug_info:
+    - Version: 5
+      UnitType:        DW_UT_compile
+      AddrSize:        8
+  debug_str_offsets:
+    - Offsets:
+        - 0x00000000
+        - 0x00000004
+        - 0x00000001
+        - 0x00000042
+    - Version: 42
+    - Length: 5
+    - Length: 8


        


More information about the llvm-commits mailing list