Skip to content

Commit ca4d8da

Browse files
committed
[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
1 parent 68f5a8b commit ca4d8da

File tree

9 files changed

+217
-48
lines changed

9 files changed

+217
-48
lines changed

lld/ELF/SyntheticSections.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -2712,8 +2712,9 @@ readPubNamesAndTypes(const LLDDwarfObj<ELFT> &obj,
27122712
for (const LLDDWARFSection *pub : {&pubNames, &pubTypes}) {
27132713
DWARFDataExtractor data(obj, *pub, config->isLE, config->wordsize);
27142714
DWARFDebugPubTable table;
2715-
if (Error e = table.extract(data, /*GnuStyle=*/true))
2715+
table.extract(data, /*GnuStyle=*/true, [&](Error e) {
27162716
warn(toString(pub->sec) + ": " + toString(std::move(e)));
2717+
});
27172718
for (const DWARFDebugPubTable::Set &set : table.getData()) {
27182719
// The value written into the constant pool is kind << 24 | cuIndex. As we
27192720
// don't know how many compilation units precede this object to compute

lld/test/ELF/Inputs/gdb-index.s

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ aaaaaaaaaaaaaaaa:
5353
.byte 0
5454

5555
.section .debug_gnu_pubnames,"",@progbits
56-
.long 0x18
56+
.long 0x24
5757
.value 0x2
5858
.long 0
5959
.long 0x33

lld/test/ELF/gdb-index-invalid-pubnames.s

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t
33
# RUN: ld.lld --gdb-index %t -o /dev/null 2>&1 | FileCheck %s
44

5-
# CHECK: warning: {{.*}}(.debug_gnu_pubnames): unexpected end of data at offset 0x1 while reading [0x0, 0x4)
5+
# CHECK: warning: {{.*}}(.debug_gnu_pubnames): name lookup table at offset 0x0 parsing failed: unexpected end of data at offset 0x1 while reading [0x0, 0x4)
66

77
.section .debug_abbrev,"",@progbits
88
.byte 1 # Abbreviation Code

lld/test/ELF/gdb-index.s

+1-1
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ entrypoint:
109109
.byte 0
110110

111111
.section .debug_gnu_pubnames,"",@progbits
112-
.long 0x18
112+
.long 0x1e
113113
.value 0x2
114114
.long 0
115115
.long 0x33

llvm/include/llvm/DebugInfo/DWARF/DWARFDebugPubTable.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ class DWARFDebugPubTable {
7373
public:
7474
DWARFDebugPubTable() = default;
7575

76-
Error extract(DWARFDataExtractor Data, bool GnuStyle);
76+
void extract(DWARFDataExtractor Data, bool GnuStyle,
77+
function_ref<void(Error)> RecoverableErrorHandler);
7778

7879
void dump(raw_ostream &OS) const;
7980

llvm/lib/DebugInfo/DWARF/DWARFContext.cpp

+1-2
Original file line numberDiff line numberDiff line change
@@ -339,8 +339,7 @@ static void dumpLoclistsSection(raw_ostream &OS, DIDumpOptions DumpOpts,
339339
static void dumpPubTableSection(raw_ostream &OS, DIDumpOptions DumpOpts,
340340
DWARFDataExtractor Data, bool GnuStyle) {
341341
DWARFDebugPubTable Table;
342-
if (Error E = Table.extract(Data, GnuStyle))
343-
DumpOpts.RecoverableErrorHandler(std::move(E));
342+
Table.extract(Data, GnuStyle, DumpOpts.RecoverableErrorHandler);
344343
Table.dump(OS);
345344
}
346345

llvm/lib/DebugInfo/DWARF/DWARFDebugPubTable.cpp

+59-15
Original file line numberDiff line numberDiff line change
@@ -11,39 +11,83 @@
1111
#include "llvm/ADT/StringRef.h"
1212
#include "llvm/BinaryFormat/Dwarf.h"
1313
#include "llvm/Support/DataExtractor.h"
14+
#include "llvm/Support/Errc.h"
1415
#include "llvm/Support/Format.h"
1516
#include "llvm/Support/raw_ostream.h"
1617
#include <cstdint>
1718

1819
using namespace llvm;
1920
using namespace dwarf;
2021

21-
Error DWARFDebugPubTable::extract(DWARFDataExtractor Data, bool GnuStyle) {
22+
void DWARFDebugPubTable::extract(
23+
DWARFDataExtractor Data, bool GnuStyle,
24+
function_ref<void(Error)> RecoverableErrorHandler) {
2225
this->GnuStyle = GnuStyle;
2326
Sets.clear();
24-
DataExtractor::Cursor C(0);
25-
while (C && Data.isValidOffset(C.tell())) {
27+
uint64_t Offset = 0;
28+
while (Data.isValidOffset(Offset)) {
29+
uint64_t SetOffset = Offset;
2630
Sets.push_back({});
27-
Set &SetData = Sets.back();
31+
Set &NewSet = Sets.back();
2832

29-
std::tie(SetData.Length, SetData.Format) = Data.getInitialLength(C);
30-
const unsigned OffsetSize = dwarf::getDwarfOffsetByteSize(SetData.Format);
33+
DataExtractor::Cursor C(Offset);
34+
std::tie(NewSet.Length, NewSet.Format) = Data.getInitialLength(C);
35+
if (!C) {
36+
// Drop the newly added set because it does not contain anything useful
37+
// to dump.
38+
Sets.pop_back();
39+
RecoverableErrorHandler(createStringError(
40+
errc::invalid_argument,
41+
"name lookup table at offset 0x%" PRIx64 " parsing failed: %s",
42+
SetOffset, toString(C.takeError()).c_str()));
43+
return;
44+
}
45+
46+
Offset = C.tell() + NewSet.Length;
47+
DWARFDataExtractor SetData(Data, Offset);
48+
const unsigned OffsetSize = dwarf::getDwarfOffsetByteSize(NewSet.Format);
49+
50+
NewSet.Version = SetData.getU16(C);
51+
NewSet.Offset = SetData.getRelocatedValue(C, OffsetSize);
52+
NewSet.Size = SetData.getUnsigned(C, OffsetSize);
3153

32-
SetData.Version = Data.getU16(C);
33-
SetData.Offset = Data.getRelocatedValue(C, OffsetSize);
34-
SetData.Size = Data.getUnsigned(C, OffsetSize);
54+
if (!C) {
55+
// Preserve the newly added set because at least some fields of the header
56+
// are read and can be dumped.
57+
RecoverableErrorHandler(
58+
createStringError(errc::invalid_argument,
59+
"name lookup table at offset 0x%" PRIx64
60+
" does not have a complete header: %s",
61+
SetOffset, toString(C.takeError()).c_str()));
62+
continue;
63+
}
3564

3665
while (C) {
37-
uint64_t DieRef = Data.getUnsigned(C, OffsetSize);
66+
uint64_t DieRef = SetData.getUnsigned(C, OffsetSize);
3867
if (DieRef == 0)
3968
break;
40-
uint8_t IndexEntryValue = GnuStyle ? Data.getU8(C) : 0;
41-
StringRef Name = Data.getCStrRef(C);
42-
SetData.Entries.push_back(
43-
{DieRef, PubIndexEntryDescriptor(IndexEntryValue), Name});
69+
uint8_t IndexEntryValue = GnuStyle ? SetData.getU8(C) : 0;
70+
StringRef Name = SetData.getCStrRef(C);
71+
if (C)
72+
NewSet.Entries.push_back(
73+
{DieRef, PubIndexEntryDescriptor(IndexEntryValue), Name});
74+
}
75+
76+
if (!C) {
77+
RecoverableErrorHandler(createStringError(
78+
errc::invalid_argument,
79+
"name lookup table at offset 0x%" PRIx64 " parsing failed: %s",
80+
SetOffset, toString(std::move(C.takeError())).c_str()));
81+
continue;
4482
}
83+
if (C.tell() != Offset)
84+
RecoverableErrorHandler(createStringError(
85+
errc::invalid_argument,
86+
"name lookup table at offset 0x%" PRIx64
87+
" has a terminator at offset 0x%" PRIx64
88+
" before the expected end at 0x%" PRIx64,
89+
SetOffset, C.tell() - OffsetSize, Offset - OffsetSize));
4590
}
46-
return C.takeError();
4791
}
4892

4993
void DWARFDebugPubTable::dump(raw_ostream &OS) const {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
# RUN: llvm-mc -triple x86_64 %s -filetype=obj -o %t
2+
3+
## All four name lookup table sections share the same parser, but slightly
4+
## different code paths are used to reach it. Do a comprehensive check for one
5+
## of the sections and minimal checks for the others.
6+
7+
# RUN: not llvm-dwarfdump -debug-gnu-pubnames %t 2> %t.err | FileCheck %s
8+
# RUN: FileCheck %s --input-file=%t.err --check-prefix=ERR
9+
10+
# RUN: not llvm-dwarfdump -debug-pubnames -debug-pubtypes -debug-gnu-pubtypes %t 2>&1 | \
11+
# RUN: FileCheck %s --check-prefix=ERR-MIN
12+
13+
.section .debug_gnu_pubnames,"",@progbits
14+
# CHECK: .debug_gnu_pubnames contents:
15+
16+
## The next few sets do not contain all required fields in the header.
17+
# 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)
18+
# CHECK-NEXT: length = 0x00000001, format = DWARF32, version = 0x0000, unit_offset = 0x00000000, unit_size = 0x00000000
19+
# CHECK-NEXT: Offset Linkage Kind Name
20+
# CHECK-NOT: 0x
21+
.long .LSet0End-.LSet0 # Length
22+
.LSet0:
23+
.byte 1 # Version (truncated)
24+
.LSet0End:
25+
26+
# 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)
27+
# CHECK-NEXT: length = 0x00000005, format = DWARF32, version = 0x0002, unit_offset = 0x00000000, unit_size = 0x00000000
28+
# CHECK-NEXT: Offset Linkage Kind Name
29+
# CHECK-NOT: 0x
30+
.long .LSet1End-.LSet1 # Length
31+
.LSet1:
32+
.short 2 # Version
33+
.byte 1, 2, 3 # Debug Info Offset (truncated)
34+
.LSet1End:
35+
36+
# 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)
37+
# CHECK-NEXT: length = 0x00000009, format = DWARF32, version = 0x0002, unit_offset = 0x00000032, unit_size = 0x00000000
38+
# CHECK-NEXT: Offset Linkage Kind Name
39+
# CHECK-NOT: 0x
40+
.long .LSet2End-.LSet2 # Length
41+
.LSet2:
42+
.short 2 # Version
43+
.long 0x32 # Debug Info Offset
44+
.byte 1, 2, 3 # Debug Info Length (truncated)
45+
.LSet2End:
46+
47+
## This set is terminated just after the header.
48+
# ERR: error: name lookup table at offset 0x1b parsing failed: unexpected end of data at offset 0x29 while reading [0x29, 0x2d)
49+
# CHECK-NEXT: length = 0x0000000a, format = DWARF32, version = 0x0002, unit_offset = 0x00000048, unit_size = 0x00000064
50+
# CHECK-NEXT: Offset Linkage Kind Name
51+
# CHECK-NOT: 0x
52+
.long .LSet3End-.LSet3 # Length
53+
.LSet3:
54+
.short 2 # Version
55+
.long 0x48 # Debug Info Offset
56+
.long 0x64 # Debug Info Length
57+
.LSet3End:
58+
59+
## The offset in the first pair is truncated.
60+
# ERR: error: name lookup table at offset 0x29 parsing failed: unexpected end of data at offset 0x3a while reading [0x37, 0x3b)
61+
# CHECK-NEXT: length = 0x0000000d, format = DWARF32, version = 0x0002, unit_offset = 0x000000ac, unit_size = 0x00000036
62+
# CHECK-NEXT: Offset Linkage Kind Name
63+
# CHECK-NOT: 0x
64+
.long .LSet4End-.LSet4 # Length
65+
.LSet4:
66+
.short 2 # Version
67+
.long 0xac # Debug Info Offset
68+
.long 0x36 # Debug Info Length
69+
.byte 1, 2, 3 # Offset (truncated)
70+
.LSet4End:
71+
72+
## The set is truncated just after the offset of the first pair.
73+
# ERR: error: name lookup table at offset 0x3a parsing failed: unexpected end of data at offset 0x4c while reading [0x4c, 0x4d)
74+
# CHECK-NEXT: length = 0x0000000e, format = DWARF32, version = 0x0002, unit_offset = 0x000000e2, unit_size = 0x00000015
75+
# CHECK-NEXT: Offset Linkage Kind Name
76+
# CHECK-NOT: 0x
77+
.long .LSet5End-.LSet5 # Length
78+
.LSet5:
79+
.short 2 # Version
80+
.long 0xe2 # Debug Info Offset
81+
.long 0x15 # Debug Info Length
82+
.long 0xf4 # Offset
83+
.LSet5End:
84+
85+
## The set is truncated just after the index entry field of the first pair.
86+
# ERR: error: name lookup table at offset 0x4c parsing failed: no null terminated string at offset 0x5f
87+
# CHECK-NEXT: length = 0x0000000f, format = DWARF32, version = 0x0002, unit_offset = 0x000000f7, unit_size = 0x00000010
88+
# CHECK-NEXT: Offset Linkage Kind Name
89+
# CHECK-NOT: 0x
90+
.long .LSet6End-.LSet6 # Length
91+
.LSet6:
92+
.short 2 # Version
93+
.long 0xf7 # Debug Info Offset
94+
.long 0x10 # Debug Info Length
95+
.long 0xf4 # Offset
96+
.byte 0x30 # Index Entry
97+
.LSet6End:
98+
99+
## This set contains a string which is not properly terminated.
100+
# ERR: error: name lookup table at offset 0x5f parsing failed: no null terminated string at offset 0x72
101+
# CHECK-NEXT: length = 0x00000012, format = DWARF32, version = 0x0002, unit_offset = 0x00000107, unit_size = 0x0000004b
102+
# CHECK-NEXT: Offset Linkage Kind Name
103+
# CHECK-NOT: 0x
104+
.long .LSet7End-.LSet7 # Length
105+
.LSet7:
106+
.short 2 # Version
107+
.long 0x107 # Debug Info Offset
108+
.long 0x4b # Debug Info Length
109+
.long 0x111 # Offset
110+
.byte 0x30 # Index Entry
111+
.ascii "foo" # The string does not terminate before the set data ends.
112+
.LSet7End:
113+
114+
## This set occupies some space after the terminator.
115+
# ERR: error: name lookup table at offset 0x75 has a terminator at offset 0x8c before the expected end at 0x8d
116+
# CHECK-NEXT: length = 0x00000018, format = DWARF32, version = 0x0002, unit_offset = 0x00000154, unit_size = 0x000002ac
117+
# CHECK-NEXT: Offset Linkage Kind Name
118+
# CHECK-NEXT: 0x0000018e EXTERNAL FUNCTION "foo"
119+
# CHECK-NOT: 0x
120+
.long .LSet8End-.LSet8 # Length
121+
.LSet8:
122+
.short 2 # Version
123+
.long 0x154 # Debug Info Offset
124+
.long 0x2ac # Debug Info Length
125+
.long 0x18e # Offset
126+
.byte 0x30 # Index Entry
127+
.asciz "foo" # Name
128+
.long 0 # Terminator
129+
.space 1
130+
.LSet8End:
131+
132+
## The remaining space in the section is too short to even contain a unit length
133+
## field.
134+
# ERR: error: name lookup table at offset 0x91 parsing failed: unexpected end of data at offset 0x94 while reading [0x91, 0x95)
135+
# CHECK-NOT: length =
136+
.space 3
137+
138+
# ERR-MIN: .debug_pubnames contents:
139+
# ERR-MIN-NEXT: error: name lookup table at offset 0x0 parsing failed: unexpected end of data at offset 0x1 while reading [0x0, 0x4)
140+
# ERR-MIN: .debug_pubtypes contents:
141+
# ERR-MIN-NEXT: error: name lookup table at offset 0x0 parsing failed: unexpected end of data at offset 0x1 while reading [0x0, 0x4)
142+
# ERR-MIN: .debug_gnu_pubtypes contents:
143+
# ERR-MIN-NEXT: error: name lookup table at offset 0x0 parsing failed: unexpected end of data at offset 0x1 while reading [0x0, 0x4)
144+
145+
.section .debug_pubnames,"",@progbits
146+
.byte 0
147+
.section .debug_pubtypes,"",@progbits
148+
.byte 0
149+
.section .debug_gnu_pubtypes,"",@progbits
150+
.byte 0

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

-26
This file was deleted.

0 commit comments

Comments
 (0)