[lld] ca4d8da - [DebugInfo] Add more checks to parsing .debug_pub* sections.

Igor Kudrin via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 9 05:16:47 PDT 2020


Author: Igor Kudrin
Date: 2020-07-09T19:15:31+07:00
New Revision: ca4d8da0c33cd9bcd05f94b4b3ac125b72be2a2a

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

LOG: [DebugInfo] Add more checks to parsing .debug_pub* sections.

The patch adds checking for various potential issues in parsing name
lookup tables and reporting them as recoverable errors, similarly as we
do for other tables.

Differential Revision: https://reviews.llvm.org/D83050

Added: 
    llvm/test/tools/llvm-dwarfdump/X86/debug_pub_tables_error_cases.s

Modified: 
    lld/ELF/SyntheticSections.cpp
    lld/test/ELF/Inputs/gdb-index.s
    lld/test/ELF/gdb-index-invalid-pubnames.s
    lld/test/ELF/gdb-index.s
    llvm/include/llvm/DebugInfo/DWARF/DWARFDebugPubTable.h
    llvm/lib/DebugInfo/DWARF/DWARFContext.cpp
    llvm/lib/DebugInfo/DWARF/DWARFDebugPubTable.cpp

Removed: 
    llvm/test/tools/llvm-dwarfdump/X86/debug_pub_tables_invalid.s


################################################################################
diff  --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp
index 33748f881576..731b9f658060 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -2712,8 +2712,9 @@ readPubNamesAndTypes(const LLDDwarfObj<ELFT> &obj,
   for (const LLDDWARFSection *pub : {&pubNames, &pubTypes}) {
     DWARFDataExtractor data(obj, *pub, config->isLE, config->wordsize);
     DWARFDebugPubTable table;
-    if (Error e = table.extract(data, /*GnuStyle=*/true))
+    table.extract(data, /*GnuStyle=*/true, [&](Error e) {
       warn(toString(pub->sec) + ": " + toString(std::move(e)));
+    });
     for (const DWARFDebugPubTable::Set &set : table.getData()) {
       // The value written into the constant pool is kind << 24 | cuIndex. As we
       // don't know how many compilation units precede this object to compute

diff  --git a/lld/test/ELF/Inputs/gdb-index.s b/lld/test/ELF/Inputs/gdb-index.s
index 794995c150f9..88474e1fdb12 100644
--- a/lld/test/ELF/Inputs/gdb-index.s
+++ b/lld/test/ELF/Inputs/gdb-index.s
@@ -53,7 +53,7 @@ aaaaaaaaaaaaaaaa:
 .byte 0
 
 .section .debug_gnu_pubnames,"", at progbits
-.long 0x18
+.long 0x24
 .value 0x2
 .long 0
 .long 0x33

diff  --git a/lld/test/ELF/gdb-index-invalid-pubnames.s b/lld/test/ELF/gdb-index-invalid-pubnames.s
index 15eb86ee7c1e..fc10dac487c5 100644
--- a/lld/test/ELF/gdb-index-invalid-pubnames.s
+++ b/lld/test/ELF/gdb-index-invalid-pubnames.s
@@ -2,7 +2,7 @@
 # RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t
 # RUN: ld.lld --gdb-index %t -o /dev/null 2>&1 | FileCheck %s
 
-# CHECK: warning: {{.*}}(.debug_gnu_pubnames): unexpected end of data at offset 0x1 while reading [0x0, 0x4)
+# CHECK: warning: {{.*}}(.debug_gnu_pubnames): name lookup table at offset 0x0 parsing failed: unexpected end of data at offset 0x1 while reading [0x0, 0x4)
 
     .section .debug_abbrev,"", at progbits
     .byte 1                         # Abbreviation Code

diff  --git a/lld/test/ELF/gdb-index.s b/lld/test/ELF/gdb-index.s
index bb8ecf34bb6a..546590ab359e 100644
--- a/lld/test/ELF/gdb-index.s
+++ b/lld/test/ELF/gdb-index.s
@@ -109,7 +109,7 @@ entrypoint:
 .byte 0
 
 .section .debug_gnu_pubnames,"", at progbits
-.long 0x18
+.long 0x1e
 .value 0x2
 .long 0
 .long 0x33

diff  --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugPubTable.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugPubTable.h
index 80c2d75bdc2a..cb347615868b 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugPubTable.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugPubTable.h
@@ -73,7 +73,8 @@ class DWARFDebugPubTable {
 public:
   DWARFDebugPubTable() = default;
 
-  Error extract(DWARFDataExtractor Data, bool GnuStyle);
+  void extract(DWARFDataExtractor Data, bool GnuStyle,
+               function_ref<void(Error)> RecoverableErrorHandler);
 
   void dump(raw_ostream &OS) const;
 

diff  --git a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp
index dba6b85e9104..bf6219497770 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp
@@ -339,8 +339,7 @@ static void dumpLoclistsSection(raw_ostream &OS, DIDumpOptions DumpOpts,
 static void dumpPubTableSection(raw_ostream &OS, DIDumpOptions DumpOpts,
                                 DWARFDataExtractor Data, bool GnuStyle) {
   DWARFDebugPubTable Table;
-  if (Error E = Table.extract(Data, GnuStyle))
-    DumpOpts.RecoverableErrorHandler(std::move(E));
+  Table.extract(Data, GnuStyle, DumpOpts.RecoverableErrorHandler);
   Table.dump(OS);
 }
 

diff  --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugPubTable.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugPubTable.cpp
index 45e16653420c..fea3b9ace8ca 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFDebugPubTable.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugPubTable.cpp
@@ -11,6 +11,7 @@
 #include "llvm/ADT/StringRef.h"
 #include "llvm/BinaryFormat/Dwarf.h"
 #include "llvm/Support/DataExtractor.h"
+#include "llvm/Support/Errc.h"
 #include "llvm/Support/Format.h"
 #include "llvm/Support/raw_ostream.h"
 #include <cstdint>
@@ -18,32 +19,75 @@
 using namespace llvm;
 using namespace dwarf;
 
-Error DWARFDebugPubTable::extract(DWARFDataExtractor Data, bool GnuStyle) {
+void DWARFDebugPubTable::extract(
+    DWARFDataExtractor Data, bool GnuStyle,
+    function_ref<void(Error)> RecoverableErrorHandler) {
   this->GnuStyle = GnuStyle;
   Sets.clear();
-  DataExtractor::Cursor C(0);
-  while (C && Data.isValidOffset(C.tell())) {
+  uint64_t Offset = 0;
+  while (Data.isValidOffset(Offset)) {
+    uint64_t SetOffset = Offset;
     Sets.push_back({});
-    Set &SetData = Sets.back();
+    Set &NewSet = Sets.back();
 
-    std::tie(SetData.Length, SetData.Format) = Data.getInitialLength(C);
-    const unsigned OffsetSize = dwarf::getDwarfOffsetByteSize(SetData.Format);
+    DataExtractor::Cursor C(Offset);
+    std::tie(NewSet.Length, NewSet.Format) = Data.getInitialLength(C);
+    if (!C) {
+      // Drop the newly added set because it does not contain anything useful
+      // to dump.
+      Sets.pop_back();
+      RecoverableErrorHandler(createStringError(
+          errc::invalid_argument,
+          "name lookup table at offset 0x%" PRIx64 " parsing failed: %s",
+          SetOffset, toString(C.takeError()).c_str()));
+      return;
+    }
+
+    Offset = C.tell() + NewSet.Length;
+    DWARFDataExtractor SetData(Data, Offset);
+    const unsigned OffsetSize = dwarf::getDwarfOffsetByteSize(NewSet.Format);
+
+    NewSet.Version = SetData.getU16(C);
+    NewSet.Offset = SetData.getRelocatedValue(C, OffsetSize);
+    NewSet.Size = SetData.getUnsigned(C, OffsetSize);
 
-    SetData.Version = Data.getU16(C);
-    SetData.Offset = Data.getRelocatedValue(C, OffsetSize);
-    SetData.Size = Data.getUnsigned(C, OffsetSize);
+    if (!C) {
+      // Preserve the newly added set because at least some fields of the header
+      // are read and can be dumped.
+      RecoverableErrorHandler(
+          createStringError(errc::invalid_argument,
+                            "name lookup table at offset 0x%" PRIx64
+                            " does not have a complete header: %s",
+                            SetOffset, toString(C.takeError()).c_str()));
+      continue;
+    }
 
     while (C) {
-      uint64_t DieRef = Data.getUnsigned(C, OffsetSize);
+      uint64_t DieRef = SetData.getUnsigned(C, OffsetSize);
       if (DieRef == 0)
         break;
-      uint8_t IndexEntryValue = GnuStyle ? Data.getU8(C) : 0;
-      StringRef Name = Data.getCStrRef(C);
-      SetData.Entries.push_back(
-          {DieRef, PubIndexEntryDescriptor(IndexEntryValue), Name});
+      uint8_t IndexEntryValue = GnuStyle ? SetData.getU8(C) : 0;
+      StringRef Name = SetData.getCStrRef(C);
+      if (C)
+        NewSet.Entries.push_back(
+            {DieRef, PubIndexEntryDescriptor(IndexEntryValue), Name});
+    }
+
+    if (!C) {
+      RecoverableErrorHandler(createStringError(
+          errc::invalid_argument,
+          "name lookup table at offset 0x%" PRIx64 " parsing failed: %s",
+          SetOffset, toString(std::move(C.takeError())).c_str()));
+      continue;
     }
+    if (C.tell() != Offset)
+      RecoverableErrorHandler(createStringError(
+          errc::invalid_argument,
+          "name lookup table at offset 0x%" PRIx64
+          " has a terminator at offset 0x%" PRIx64
+          " before the expected end at 0x%" PRIx64,
+          SetOffset, C.tell() - OffsetSize, Offset - OffsetSize));
   }
-  return C.takeError();
 }
 
 void DWARFDebugPubTable::dump(raw_ostream &OS) const {

diff  --git a/llvm/test/tools/llvm-dwarfdump/X86/debug_pub_tables_error_cases.s b/llvm/test/tools/llvm-dwarfdump/X86/debug_pub_tables_error_cases.s
new file mode 100644
index 000000000000..8daea1e0f80d
--- /dev/null
+++ b/llvm/test/tools/llvm-dwarfdump/X86/debug_pub_tables_error_cases.s
@@ -0,0 +1,150 @@
+# RUN: llvm-mc -triple x86_64 %s -filetype=obj -o %t
+
+## All four name lookup table sections share the same parser, but slightly
+## 
diff erent code paths are used to reach it. Do a comprehensive check for one
+## of the sections and minimal checks for the others.
+
+# RUN: not llvm-dwarfdump -debug-gnu-pubnames %t 2> %t.err | FileCheck %s
+# RUN: FileCheck %s --input-file=%t.err --check-prefix=ERR
+
+# RUN: not llvm-dwarfdump -debug-pubnames -debug-pubtypes -debug-gnu-pubtypes %t 2>&1 | \
+# RUN:   FileCheck %s --check-prefix=ERR-MIN
+
+    .section .debug_gnu_pubnames,"", at progbits
+# CHECK:      .debug_gnu_pubnames contents:
+
+## The next few sets do not contain all required fields in the header.
+# ERR: error: name lookup table at offset 0x0 does not have a complete header: unexpected end of data at offset 0x5 while reading [0x4, 0x6)
+# CHECK-NEXT: length = 0x00000001, format = DWARF32, version = 0x0000, unit_offset = 0x00000000, unit_size = 0x00000000
+# CHECK-NEXT: Offset Linkage Kind Name
+# CHECK-NOT:  0x
+    .long .LSet0End-.LSet0      # Length
+.LSet0:
+    .byte 1                     # Version (truncated)
+.LSet0End:
+
+# ERR: error: name lookup table at offset 0x5 does not have a complete header: unexpected end of data at offset 0xe while reading [0xb, 0xf)
+# CHECK-NEXT: length = 0x00000005, format = DWARF32, version = 0x0002, unit_offset = 0x00000000, unit_size = 0x00000000
+# CHECK-NEXT: Offset Linkage Kind Name
+# CHECK-NOT:  0x
+    .long .LSet1End-.LSet1      # Length
+.LSet1:
+    .short 2                    # Version
+    .byte 1, 2, 3               # Debug Info Offset (truncated)
+.LSet1End:
+
+# ERR: error: name lookup table at offset 0xe does not have a complete header: unexpected end of data at offset 0x1b while reading [0x18, 0x1c)
+# CHECK-NEXT: length = 0x00000009, format = DWARF32, version = 0x0002, unit_offset = 0x00000032, unit_size = 0x00000000
+# CHECK-NEXT: Offset Linkage Kind Name
+# CHECK-NOT:  0x
+    .long .LSet2End-.LSet2      # Length
+.LSet2:
+    .short 2                    # Version
+    .long 0x32                  # Debug Info Offset
+    .byte 1, 2, 3               # Debug Info Length (truncated)
+.LSet2End:
+
+## This set is terminated just after the header.
+# ERR: error: name lookup table at offset 0x1b parsing failed: unexpected end of data at offset 0x29 while reading [0x29, 0x2d)
+# CHECK-NEXT: length = 0x0000000a, format = DWARF32, version = 0x0002, unit_offset = 0x00000048, unit_size = 0x00000064
+# CHECK-NEXT: Offset Linkage Kind Name
+# CHECK-NOT:  0x
+    .long .LSet3End-.LSet3      # Length
+.LSet3:
+    .short 2                    # Version
+    .long 0x48                  # Debug Info Offset
+    .long 0x64                  # Debug Info Length
+.LSet3End:
+
+## The offset in the first pair is truncated.
+# ERR: error: name lookup table at offset 0x29 parsing failed: unexpected end of data at offset 0x3a while reading [0x37, 0x3b)
+# CHECK-NEXT: length = 0x0000000d, format = DWARF32, version = 0x0002, unit_offset = 0x000000ac, unit_size = 0x00000036
+# CHECK-NEXT: Offset Linkage Kind Name
+# CHECK-NOT:  0x
+    .long .LSet4End-.LSet4      # Length
+.LSet4:
+    .short 2                    # Version
+    .long 0xac                  # Debug Info Offset
+    .long 0x36                  # Debug Info Length
+    .byte 1, 2, 3               # Offset (truncated)
+.LSet4End:
+
+## The set is truncated just after the offset of the first pair.
+# ERR: error: name lookup table at offset 0x3a parsing failed: unexpected end of data at offset 0x4c while reading [0x4c, 0x4d)
+# CHECK-NEXT: length = 0x0000000e, format = DWARF32, version = 0x0002, unit_offset = 0x000000e2, unit_size = 0x00000015
+# CHECK-NEXT: Offset Linkage Kind Name
+# CHECK-NOT:  0x
+    .long .LSet5End-.LSet5      # Length
+.LSet5:
+    .short 2                    # Version
+    .long 0xe2                  # Debug Info Offset
+    .long 0x15                  # Debug Info Length
+    .long 0xf4                  # Offset
+.LSet5End:
+
+## The set is truncated just after the index entry field of the first pair.
+# ERR: error: name lookup table at offset 0x4c parsing failed: no null terminated string at offset 0x5f
+# CHECK-NEXT: length = 0x0000000f, format = DWARF32, version = 0x0002, unit_offset = 0x000000f7, unit_size = 0x00000010
+# CHECK-NEXT: Offset Linkage Kind Name
+# CHECK-NOT:  0x
+    .long .LSet6End-.LSet6      # Length
+.LSet6:
+    .short 2                    # Version
+    .long 0xf7                  # Debug Info Offset
+    .long 0x10                  # Debug Info Length
+    .long 0xf4                  # Offset
+    .byte 0x30                  # Index Entry
+.LSet6End:
+
+## This set contains a string which is not properly terminated.
+# ERR: error: name lookup table at offset 0x5f parsing failed: no null terminated string at offset 0x72
+# CHECK-NEXT: length = 0x00000012, format = DWARF32, version = 0x0002, unit_offset = 0x00000107, unit_size = 0x0000004b
+# CHECK-NEXT: Offset Linkage Kind Name
+# CHECK-NOT:  0x
+    .long .LSet7End-.LSet7      # Length
+.LSet7:
+    .short 2                    # Version
+    .long 0x107                 # Debug Info Offset
+    .long 0x4b                  # Debug Info Length
+    .long 0x111                 # Offset
+    .byte 0x30                  # Index Entry
+    .ascii "foo"                # The string does not terminate before the set data ends.
+.LSet7End:
+
+## This set occupies some space after the terminator.
+# ERR: error: name lookup table at offset 0x75 has a terminator at offset 0x8c before the expected end at 0x8d
+# CHECK-NEXT: length = 0x00000018, format = DWARF32, version = 0x0002, unit_offset = 0x00000154, unit_size = 0x000002ac
+# CHECK-NEXT: Offset Linkage Kind Name
+# CHECK-NEXT: 0x0000018e EXTERNAL FUNCTION "foo"
+# CHECK-NOT:  0x
+    .long .LSet8End-.LSet8      # Length
+.LSet8:
+    .short 2                    # Version
+    .long 0x154                 # Debug Info Offset
+    .long 0x2ac                 # Debug Info Length
+    .long 0x18e                 # Offset
+    .byte 0x30                  # Index Entry
+    .asciz "foo"                # Name
+    .long 0                     # Terminator
+    .space 1
+.LSet8End:
+
+## The remaining space in the section is too short to even contain a unit length
+## field.
+# ERR: error: name lookup table at offset 0x91 parsing failed: unexpected end of data at offset 0x94 while reading [0x91, 0x95)
+# CHECK-NOT:  length =
+    .space 3
+
+# ERR-MIN:      .debug_pubnames contents:
+# ERR-MIN-NEXT: error: name lookup table at offset 0x0 parsing failed: unexpected end of data at offset 0x1 while reading [0x0, 0x4)
+# ERR-MIN:      .debug_pubtypes contents:
+# ERR-MIN-NEXT: error: name lookup table at offset 0x0 parsing failed: unexpected end of data at offset 0x1 while reading [0x0, 0x4)
+# ERR-MIN:      .debug_gnu_pubtypes contents:
+# ERR-MIN-NEXT: error: name lookup table at offset 0x0 parsing failed: unexpected end of data at offset 0x1 while reading [0x0, 0x4)
+
+    .section .debug_pubnames,"", at progbits
+    .byte 0
+    .section .debug_pubtypes,"", at progbits
+    .byte 0
+    .section .debug_gnu_pubtypes,"", at progbits
+    .byte 0

diff  --git a/llvm/test/tools/llvm-dwarfdump/X86/debug_pub_tables_invalid.s b/llvm/test/tools/llvm-dwarfdump/X86/debug_pub_tables_invalid.s
deleted file mode 100644
index cfd75c2a9aca..000000000000
--- a/llvm/test/tools/llvm-dwarfdump/X86/debug_pub_tables_invalid.s
+++ /dev/null
@@ -1,26 +0,0 @@
-# RUN: llvm-mc -triple x86_64 %s -filetype=obj -o %t
-# RUN: not llvm-dwarfdump -v %t 2>&1 | FileCheck %s
-
-# CHECK:      .debug_pubnames contents:
-# CHECK-NEXT: error: unexpected end of data at offset 0x1 while reading [0x0, 0x4)
-
-# CHECK:      .debug_pubtypes contents:
-# CHECK-NEXT: error: unexpected end of data at offset 0x1 while reading [0x0, 0x4)
-
-# CHECK:      .debug_gnu_pubnames contents:
-# CHECK-NEXT: error: unexpected end of data at offset 0x1 while reading [0x0, 0x4)
-
-# CHECK:      .debug_gnu_pubtypes contents:
-# CHECK-NEXT: error: unexpected end of data at offset 0x1 while reading [0x0, 0x4)
-
-    .section .debug_pubnames,"", at progbits
-    .byte 0
-
-    .section .debug_pubtypes,"", at progbits
-    .byte 0
-
-    .section .debug_gnu_pubnames,"", at progbits
-    .byte 0
-
-    .section .debug_gnu_pubtypes,"", at progbits
-    .byte 0


        


More information about the llvm-commits mailing list