Skip to content

Commit 0d8cb8b

Browse files
committed
DWARFVerifier: Verify CU/TU index overlap issues
Discovered in a large object that would need a 64 bit index (but the cu/tu index format doesn't include a 64 bit offset/length mode in DWARF64 - a spec bug) but instead binutils dwp overflowed the offsets causing overlapping regions.
1 parent 18fd09a commit 0d8cb8b

File tree

5 files changed

+198
-0
lines changed

5 files changed

+198
-0
lines changed

llvm/include/llvm/DebugInfo/DWARF/DWARFUnitIndex.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,25 @@ enum DWARFSectionKind {
6464
DW_SECT_EXT_MACINFO = 10,
6565
};
6666

67+
inline const char *toString(DWARFSectionKind Kind) {
68+
switch (Kind) {
69+
case DW_SECT_EXT_unknown:
70+
return "Unknown DW_SECT value 0";
71+
#define STRINGIZE(X) #X
72+
#define HANDLE_DW_SECT(ID, NAME) \
73+
case DW_SECT_##NAME: \
74+
return "DW_SECT_" STRINGIZE(NAME);
75+
#include "llvm/BinaryFormat/Dwarf.def"
76+
case DW_SECT_EXT_TYPES:
77+
return "DW_SECT_TYPES";
78+
case DW_SECT_EXT_LOC:
79+
return "DW_SECT_LOC";
80+
case DW_SECT_EXT_MACINFO:
81+
return "DW_SECT_MACINFO";
82+
}
83+
llvm_unreachable("unknown DWARFSectionKind");
84+
}
85+
6786
/// Convert the internal value for a section kind to an on-disk value.
6887
///
6988
/// The conversion depends on the version of the index section.

llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h"
1515
#include "llvm/DebugInfo/DWARF/DWARFAddressRange.h"
1616
#include "llvm/DebugInfo/DWARF/DWARFDie.h"
17+
#include "llvm/DebugInfo/DWARF/DWARFUnitIndex.h"
1718
#include <cstdint>
1819
#include <map>
1920
#include <set>
@@ -156,6 +157,10 @@ class DWARFVerifier {
156157
unsigned verifyUnitSection(const DWARFSection &S);
157158
unsigned verifyUnits(const DWARFUnitVector &Units);
158159

160+
unsigned verifyIndexes(const DWARFObject &DObj);
161+
unsigned verifyIndex(StringRef Name, DWARFSectionKind SectionKind,
162+
StringRef Index);
163+
159164
/// Verifies that a call site entry is nested within a subprogram with a
160165
/// DW_AT_call attribute.
161166
///
@@ -300,6 +305,24 @@ class DWARFVerifier {
300305
/// \returns true if all sections verify successfully, false otherwise.
301306
bool handleDebugInfo();
302307

308+
/// Verify the information in the .debug_cu_index section.
309+
///
310+
/// Any errors are reported to the stream that was this object was
311+
/// constructed with.
312+
///
313+
/// \returns true if the .debug_cu_index verifies successfully, false
314+
/// otherwise.
315+
bool handleDebugCUIndex();
316+
317+
/// Verify the information in the .debug_tu_index section.
318+
///
319+
/// Any errors are reported to the stream that was this object was
320+
/// constructed with.
321+
///
322+
/// \returns true if the .debug_tu_index verifies successfully, false
323+
/// otherwise.
324+
bool handleDebugTUIndex();
325+
303326
/// Verify the information in the .debug_line section.
304327
///
305328
/// Any errors are reported to the stream that was this object was

llvm/lib/DebugInfo/DWARF/DWARFContext.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -769,6 +769,10 @@ bool DWARFContext::verify(raw_ostream &OS, DIDumpOptions DumpOpts) {
769769
DWARFVerifier verifier(OS, *this, DumpOpts);
770770

771771
Success &= verifier.handleDebugAbbrev();
772+
if (DumpOpts.DumpType & DIDT_DebugCUIndex)
773+
Success &= verifier.handleDebugCUIndex();
774+
if (DumpOpts.DumpType & DIDT_DebugTUIndex)
775+
Success &= verifier.handleDebugTUIndex();
772776
if (DumpOpts.DumpType & DIDT_DebugInfo)
773777
Success &= verifier.handleDebugInfo();
774778
if (DumpOpts.DumpType & DIDT_DebugLine)

llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
//
77
//===----------------------------------------------------------------------===//
88
#include "llvm/DebugInfo/DWARF/DWARFVerifier.h"
9+
#include "llvm/ADT/IntervalMap.h"
910
#include "llvm/ADT/SmallSet.h"
1011
#include "llvm/BinaryFormat/Dwarf.h"
1112
#include "llvm/DebugInfo/DWARF/DWARFAbbreviationDeclaration.h"
@@ -395,6 +396,57 @@ unsigned DWARFVerifier::verifyUnitSection(const DWARFSection &S) {
395396
return NumDebugInfoErrors;
396397
}
397398

399+
unsigned DWARFVerifier::verifyIndex(StringRef Name,
400+
DWARFSectionKind InfoColumnKind,
401+
StringRef IndexStr) {
402+
if (IndexStr.empty())
403+
return 0;
404+
OS << "Verifying " << Name << "...\n";
405+
DWARFUnitIndex Index(InfoColumnKind);
406+
DataExtractor D(IndexStr, DCtx.isLittleEndian(), 0);
407+
if (!Index.parse(D))
408+
return 1;
409+
IntervalMap<uint32_t, uint64_t>::Allocator Alloc;
410+
std::vector<IntervalMap<uint32_t, uint64_t>> Sections(
411+
Index.getColumnKinds().size(), IntervalMap<uint32_t, uint64_t>(Alloc));
412+
for (const DWARFUnitIndex::Entry &E : Index.getRows()) {
413+
uint64_t Sig = E.getSignature();
414+
if (!E.getContributions())
415+
continue;
416+
for (auto E : enumerate(InfoColumnKind == DW_SECT_INFO
417+
? makeArrayRef(E.getContributions(),
418+
Index.getColumnKinds().size())
419+
: makeArrayRef(E.getContribution(), 1))) {
420+
const DWARFUnitIndex::Entry::SectionContribution &SC = E.value();
421+
int Col = E.index();
422+
if (SC.Length == 0)
423+
continue;
424+
auto &M = Sections[Col];
425+
auto I = M.find(SC.Offset);
426+
if (I != M.end() && I.start() < (SC.Offset + SC.Length)) {
427+
error() << llvm::formatv(
428+
"overlapping index entries for entries {0:x16} "
429+
"and {1:x16} for column {2}\n",
430+
*I, Sig, toString(Index.getColumnKinds()[Col]));
431+
return 1;
432+
}
433+
M.insert(SC.Offset, SC.Offset + SC.Length - 1, Sig);
434+
}
435+
}
436+
437+
return 0;
438+
}
439+
440+
bool DWARFVerifier::handleDebugCUIndex() {
441+
return verifyIndex(".debug_cu_index", DWARFSectionKind::DW_SECT_INFO,
442+
DCtx.getDWARFObj().getCUIndexSection()) == 0;
443+
}
444+
445+
bool DWARFVerifier::handleDebugTUIndex() {
446+
return verifyIndex(".debug_tu_index", DWARFSectionKind::DW_SECT_EXT_TYPES,
447+
DCtx.getDWARFObj().getTUIndexSection()) == 0;
448+
}
449+
398450
bool DWARFVerifier::handleDebugInfo() {
399451
const DWARFObject &DObj = DCtx.getDWARFObj();
400452
unsigned NumErrors = 0;
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# RUN: llvm-mc -triple x86_64-unknown-linux %s -filetype=obj -o - | \
2+
# RUN: not llvm-dwarfdump -debug-cu-index -debug-tu-index --verify - | FileCheck %s
3+
4+
# FIXME: The verifier should probably be handled to verify the hash table
5+
# itself - in which case this test would need to be updated to have a correct
6+
# hash table (currently hand crafted with no attempt at correct allocation of
7+
# hashes to buckets) - and probably to verify that the section ranges apply to
8+
# sections that exist, which currently they don't
9+
10+
# This tests that an index that describes units as being in overlapping
11+
# sections is invalid (this was observed in the wild due to overflow due to the
12+
# 32 bit limit of the indexes (a DWARF spec bug - there should be a 64 bit
13+
# version of the index format with 64 bit offsets/sizes)) - but Type Units will
14+
# generally share all the sections other than the info section with each other
15+
# (and with their originating CU) since the dwo format has no way to describe
16+
# which part of non-info-section contributions are used by which units, so
17+
# they're all shared. So demonstrate that the TU index ignores non-info overlap,
18+
# but the CU index diagnoses such overlap (in the abbrev section, in this case)
19+
20+
# This doesn't currently check for info section overlap between the CU and TU
21+
# index, but that could be an extension of this work in the future.
22+
23+
# CHECK: Verifying .debug_cu_index...
24+
# CHECK: error: overlapping index entries for entries 0x0000000000000001 and 0x0000000000000002 for column DW_SECT_ABBREV
25+
# CHECK: Verifying .debug_tu_index...
26+
# CHECK: error: overlapping index entries for entries 0x0000000000000001 and 0x0000000000000003 for column DW_SECT_INFO
27+
28+
.section .debug_cu_index, "", @progbits
29+
## Header:
30+
.long 5 # Version
31+
.long 2 # Section count
32+
.long 3 # Unit count
33+
.long 4 # Slot count
34+
## Hash Table of Signatures:
35+
.quad 0x0000000000000001
36+
.quad 0x0000000000000002
37+
.quad 0x0000000000000003
38+
.quad 0
39+
## Parallel Table of Indexes:
40+
.long 1
41+
.long 2
42+
.long 3
43+
.long 0
44+
## Table of Section Offsets:
45+
## Row 0:
46+
.long 1 # DW_SECT_INFO
47+
.long 3 # DW_SECT_ABBREV
48+
## Row 1:
49+
.long 0x1 # Offset in .debug_info.dwo
50+
.long 0x1 # Offset in .debug_abbrev.dwo
51+
## Row 2:
52+
.long 0x2 # Offset in .debug_info.dwo
53+
.long 0x1 # Offset in .debug_abbrev.dwo
54+
## Row 3:
55+
.long 0x1 # Offset in .debug_info.dwo
56+
.long 0x1 # Offset in .debug_abbrev.dwo
57+
## Table of Section Sizes:
58+
.long 0x1 # Size in .debug_info.dwo
59+
.long 0x1 # Size in .debug_abbrev.dwo
60+
.long 0x1 # Size in .debug_info.dwo
61+
.long 0x1 # Size in .debug_abbrev.dwo
62+
.long 0x1 # Size in .debug_info.dwo
63+
.long 0x1 # Size in .debug_abbrev.dwo
64+
65+
.section .debug_tu_index, "", @progbits
66+
## Header:
67+
.long 5 # Version
68+
.long 2 # Section count
69+
.long 3 # Unit count
70+
.long 4 # Slot count
71+
## Hash Table of Signatures:
72+
.quad 0x0000000000000001
73+
.quad 0x0000000000000002
74+
.quad 0x0000000000000003
75+
.quad 0
76+
## Parallel Table of Indexes:
77+
.long 1
78+
.long 2
79+
.long 3
80+
.long 0
81+
## Table of Section Offsets:
82+
## Row 0:
83+
.long 1 # DW_SECT_INFO
84+
.long 3 # DW_SECT_ABBREV
85+
## Row 1:
86+
.long 0x1 # Offset in .debug_info.dwo
87+
.long 0x1 # Offset in .debug_abbrev.dwo
88+
## Row 2:
89+
.long 0x2 # Offset in .debug_info.dwo
90+
.long 0x1 # Offset in .debug_abbrev.dwo
91+
## Row 3:
92+
.long 0x1 # Offset in .debug_info.dwo
93+
.long 0x1 # Offset in .debug_abbrev.dwo
94+
## Table of Section Sizes:
95+
.long 0x1 # Size in .debug_info.dwo
96+
.long 0x1 # Size in .debug_abbrev.dwo
97+
.long 0x1 # Size in .debug_info.dwo
98+
.long 0x1 # Size in .debug_abbrev.dwo
99+
.long 0x1 # Size in .debug_info.dwo
100+
.long 0x1 # Size in .debug_abbrev.dwo

0 commit comments

Comments
 (0)