Skip to content
This repository was archived by the owner on Apr 23, 2020. It is now read-only.

Commit edadbde

Browse files
author
Greg Clayton
committed
Verify that all references point to actual DIEs in "llvm-dwarfdump --verify"
LTO and other fancy linking previously led to DWARF that contained invalid references. We already validate that CU relative references fall into the CU, and the DW_FORM_ref_addr references fall inside the .debug_info section, but we didn't validate that the references pointed to correct DIE offsets. This new verification will ensure that all references refer to actual DIEs and not an offset in between. This caught a bug in DWARFUnit::getDIEForOffset() where if you gave it any offset, it would match the DIE that mathes the offset _or_ the next DIE. This has been fixed. Differential Revision: https://reviews.llvm.org/D32722 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@301971 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent dbb0e70 commit edadbde

File tree

4 files changed

+204
-84
lines changed

4 files changed

+204
-84
lines changed

include/llvm/DebugInfo/DWARF/DWARFContext.h

+3
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,9 @@ class DWARFContext : public DIContext {
172172
return DWOCUs[index].get();
173173
}
174174

175+
/// Get a DIE given an exact offset.
176+
DWARFDie getDIEForOffset(uint32_t Offset);
177+
175178
const DWARFUnitIndex &getCUIndex();
176179
DWARFGdbIndex &getGdbIndex();
177180
const DWARFUnitIndex &getTUIndex();

include/llvm/DebugInfo/DWARF/DWARFUnit.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -312,9 +312,9 @@ class DWARFUnit {
312312
[](const DWARFDebugInfoEntry &LHS, uint32_t Offset) {
313313
return LHS.getOffset() < Offset;
314314
});
315-
if (it == DieArray.end())
316-
return DWARFDie();
317-
return DWARFDie(this, &*it);
315+
if (it != DieArray.end() && it->getOffset() == Offset)
316+
return DWARFDie(this, &*it);
317+
return DWARFDie();
318318
}
319319

320320
uint32_t getLineTableOffset() const {

lib/DebugInfo/DWARF/DWARFContext.cpp

+142-81
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@
4242
#include "llvm/Support/raw_ostream.h"
4343
#include <algorithm>
4444
#include <cstdint>
45+
#include <map>
46+
#include <set>
4547
#include <string>
4648
#include <utility>
4749
#include <vector>
@@ -284,11 +286,30 @@ void DWARFContext::dump(raw_ostream &OS, DIDumpType DumpType, bool DumpEH,
284286
getStringSection(), isLittleEndian());
285287
}
286288

287-
bool DWARFContext::verify(raw_ostream &OS, DIDumpType DumpType) {
288-
bool Success = true;
289-
if (DumpType == DIDT_All || DumpType == DIDT_Info) {
289+
DWARFDie DWARFContext::getDIEForOffset(uint32_t Offset) {
290+
parseCompileUnits();
291+
if (auto *CU = CUs.getUnitForOffset(Offset))
292+
return CU->getDIEForOffset(Offset);
293+
return DWARFDie();
294+
}
295+
296+
namespace {
297+
298+
class Verifier {
299+
raw_ostream &OS;
300+
DWARFContext &DCtx;
301+
public:
302+
Verifier(raw_ostream &S, DWARFContext &D) : OS(S), DCtx(D) {}
303+
304+
bool HandleDebugInfo() {
305+
bool Success = true;
306+
// A map that tracks all references (converted absolute references) so we
307+
// can verify each reference points to a valid DIE and not an offset that
308+
// lies between to valid DIEs.
309+
std::map<uint64_t, std::set<uint32_t>> ReferenceToDIEOffsets;
310+
290311
OS << "Verifying .debug_info...\n";
291-
for (const auto &CU : compile_units()) {
312+
for (const auto &CU : DCtx.compile_units()) {
292313
unsigned NumDies = CU->getNumDIEs();
293314
for (unsigned I = 0; I < NumDies; ++I) {
294315
auto Die = CU->getDIEAtIndex(I);
@@ -299,101 +320,141 @@ bool DWARFContext::verify(raw_ostream &OS, DIDumpType DumpType) {
299320
const auto Attr = AttrValue.Attr;
300321
const auto Form = AttrValue.Value.getForm();
301322
switch (Attr) {
302-
case DW_AT_ranges:
303-
// Make sure the offset in the DW_AT_ranges attribute is valid.
304-
if (auto SectionOffset = AttrValue.Value.getAsSectionOffset()) {
305-
if (*SectionOffset >= getRangeSection().Data.size()) {
323+
case DW_AT_ranges:
324+
// Make sure the offset in the DW_AT_ranges attribute is valid.
325+
if (auto SectionOffset = AttrValue.Value.getAsSectionOffset()) {
326+
if (*SectionOffset >= DCtx.getRangeSection().Data.size()) {
327+
Success = false;
328+
OS << "error: DW_AT_ranges offset is beyond .debug_ranges "
329+
"bounds:\n";
330+
Die.dump(OS, 0);
331+
OS << "\n";
332+
}
333+
} else {
306334
Success = false;
307-
OS << "error: DW_AT_ranges offset is beyond .debug_ranges "
308-
"bounds:\n";
335+
OS << "error: DIE has invalid DW_AT_ranges encoding:\n";
309336
Die.dump(OS, 0);
310337
OS << "\n";
311338
}
312-
} else {
313-
Success = false;
314-
OS << "error: DIE has invalid DW_AT_ranges encoding:\n";
315-
Die.dump(OS, 0);
316-
OS << "\n";
317-
}
318-
break;
319-
case DW_AT_stmt_list:
320-
// Make sure the offset in the DW_AT_stmt_list attribute is valid.
321-
if (auto SectionOffset = AttrValue.Value.getAsSectionOffset()) {
322-
if (*SectionOffset >= getLineSection().Data.size()) {
339+
break;
340+
case DW_AT_stmt_list:
341+
// Make sure the offset in the DW_AT_stmt_list attribute is valid.
342+
if (auto SectionOffset = AttrValue.Value.getAsSectionOffset()) {
343+
if (*SectionOffset >= DCtx.getLineSection().Data.size()) {
344+
Success = false;
345+
OS << "error: DW_AT_stmt_list offset is beyond .debug_line "
346+
"bounds: "
347+
<< format("0x%08" PRIx32, *SectionOffset) << "\n";
348+
CU->getUnitDIE().dump(OS, 0);
349+
OS << "\n";
350+
}
351+
} else {
323352
Success = false;
324-
OS << "error: DW_AT_stmt_list offset is beyond .debug_line "
325-
"bounds: "
326-
<< format("0x%08" PRIx32, *SectionOffset) << "\n";
327-
CU->getUnitDIE().dump(OS, 0);
353+
OS << "error: DIE has invalid DW_AT_stmt_list encoding:\n";
354+
Die.dump(OS, 0);
328355
OS << "\n";
329356
}
330-
} else {
331-
Success = false;
332-
OS << "error: DIE has invalid DW_AT_stmt_list encoding:\n";
333-
Die.dump(OS, 0);
334-
OS << "\n";
335-
}
336-
break;
337-
338-
default:
339-
break;
357+
break;
358+
359+
default:
360+
break;
340361
}
341362
switch (Form) {
342-
case DW_FORM_ref1:
343-
case DW_FORM_ref2:
344-
case DW_FORM_ref4:
345-
case DW_FORM_ref8:
346-
case DW_FORM_ref_udata: {
347-
// Verify all CU relative references are valid CU offsets.
348-
Optional<uint64_t> RefVal = AttrValue.Value.getAsReference();
349-
assert(RefVal);
350-
if (RefVal) {
351-
auto DieCU = Die.getDwarfUnit();
352-
auto CUSize = DieCU->getNextUnitOffset() - DieCU->getOffset();
353-
auto CUOffset = AttrValue.Value.getRawUValue();
354-
if (CUOffset >= CUSize) {
363+
case DW_FORM_ref1:
364+
case DW_FORM_ref2:
365+
case DW_FORM_ref4:
366+
case DW_FORM_ref8:
367+
case DW_FORM_ref_udata: {
368+
// Verify all CU relative references are valid CU offsets.
369+
Optional<uint64_t> RefVal = AttrValue.Value.getAsReference();
370+
assert(RefVal);
371+
if (RefVal) {
372+
auto DieCU = Die.getDwarfUnit();
373+
auto CUSize = DieCU->getNextUnitOffset() - DieCU->getOffset();
374+
auto CUOffset = AttrValue.Value.getRawUValue();
375+
if (CUOffset >= CUSize) {
376+
Success = false;
377+
OS << "error: " << FormEncodingString(Form) << " CU offset "
378+
<< format("0x%08" PRIx32, CUOffset)
379+
<< " is invalid (must be less than CU size of "
380+
<< format("0x%08" PRIx32, CUSize) << "):\n";
381+
Die.dump(OS, 0);
382+
OS << "\n";
383+
} else {
384+
// Valid reference, but we will verify it points to an actual
385+
// DIE later.
386+
ReferenceToDIEOffsets[*RefVal].insert(Die.getOffset());
387+
}
388+
}
389+
break;
390+
}
391+
case DW_FORM_ref_addr: {
392+
// Verify all absolute DIE references have valid offsets in the
393+
// .debug_info section.
394+
Optional<uint64_t> RefVal = AttrValue.Value.getAsReference();
395+
assert(RefVal);
396+
if (RefVal) {
397+
if(*RefVal >= DCtx.getInfoSection().Data.size()) {
398+
Success = false;
399+
OS << "error: DW_FORM_ref_addr offset beyond .debug_info "
400+
"bounds:\n";
401+
Die.dump(OS, 0);
402+
OS << "\n";
403+
} else {
404+
// Valid reference, but we will verify it points to an actual
405+
// DIE later.
406+
ReferenceToDIEOffsets[*RefVal].insert(Die.getOffset());
407+
}
408+
}
409+
break;
410+
}
411+
case DW_FORM_strp: {
412+
auto SecOffset = AttrValue.Value.getAsSectionOffset();
413+
assert(SecOffset); // DW_FORM_strp is a section offset.
414+
if (SecOffset && *SecOffset >= DCtx.getStringSection().size()) {
355415
Success = false;
356-
OS << "error: " << FormEncodingString(Form) << " CU offset "
357-
<< format("0x%08" PRIx32, CUOffset)
358-
<< " is invalid (must be less than CU size of "
359-
<< format("0x%08" PRIx32, CUSize) << "):\n";
416+
OS << "error: DW_FORM_strp offset beyond .debug_str bounds:\n";
360417
Die.dump(OS, 0);
361418
OS << "\n";
362419
}
420+
break;
363421
}
364-
break;
365-
}
366-
case DW_FORM_ref_addr: {
367-
// Verify all absolute DIE references have valid offsets in the
368-
// .debug_info section.
369-
Optional<uint64_t> RefVal = AttrValue.Value.getAsReference();
370-
assert(RefVal);
371-
if (RefVal && *RefVal >= getInfoSection().Data.size()) {
372-
Success = false;
373-
OS << "error: DW_FORM_ref_addr offset beyond .debug_info "
374-
"bounds:\n";
375-
Die.dump(OS, 0);
376-
OS << "\n";
377-
}
378-
break;
379-
}
380-
case DW_FORM_strp: {
381-
auto SecOffset = AttrValue.Value.getAsSectionOffset();
382-
assert(SecOffset); // DW_FORM_strp is a section offset.
383-
if (SecOffset && *SecOffset >= getStringSection().size()) {
384-
Success = false;
385-
OS << "error: DW_FORM_strp offset beyond .debug_str bounds:\n";
386-
Die.dump(OS, 0);
387-
OS << "\n";
388-
}
389-
break;
390-
}
391-
default:
392-
break;
422+
default:
423+
break;
393424
}
394425
}
395426
}
396427
}
428+
429+
// Take all references and make sure they point to an actual DIE by
430+
// getting the DIE by offset and emitting an error
431+
OS << "Verifying .debug_info references...\n";
432+
for (auto Pair: ReferenceToDIEOffsets) {
433+
auto Die = DCtx.getDIEForOffset(Pair.first);
434+
if (Die)
435+
continue;
436+
Success = false;
437+
OS << "error: invalid DIE reference " << format("0x%08" PRIx64, Pair.first)
438+
<< ". Offset is in between DIEs:\n";
439+
for (auto Offset: Pair.second) {
440+
auto ReferencingDie = DCtx.getDIEForOffset(Offset);
441+
ReferencingDie.dump(OS, 0);
442+
OS << "\n";
443+
}
444+
OS << "\n";
445+
}
446+
return Success;
447+
}
448+
};
449+
450+
} // anonymous namespace
451+
452+
bool DWARFContext::verify(raw_ostream &OS, DIDumpType DumpType) {
453+
bool Success = true;
454+
Verifier verifier(OS, *this);
455+
if (DumpType == DIDT_All || DumpType == DIDT_Info) {
456+
if (!verifier.HandleDebugInfo())
457+
Success = false;
397458
}
398459
return Success;
399460
}

unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp

+56
Original file line numberDiff line numberDiff line change
@@ -1908,4 +1908,60 @@ TEST(DWARFDebugInfo, TestDwarfVerifyInvalidStrp) {
19081908
EXPECT_TRUE(strm.str().find(err) != std::string::npos);
19091909
}
19101910

1911+
TEST(DWARFDebugInfo, TestDwarfVerifyInvalidRefAddrBetween) {
1912+
// Create a single compile unit with a single function that has a DW_AT_type
1913+
// with a valid .debug_info offset, but the offset is between two DIEs.
1914+
const char *yamldata = R"(
1915+
debug_str:
1916+
- ''
1917+
- /tmp/main.c
1918+
- main
1919+
debug_abbrev:
1920+
- Code: 0x00000001
1921+
Tag: DW_TAG_compile_unit
1922+
Children: DW_CHILDREN_yes
1923+
Attributes:
1924+
- Attribute: DW_AT_name
1925+
Form: DW_FORM_strp
1926+
- Code: 0x00000002
1927+
Tag: DW_TAG_subprogram
1928+
Children: DW_CHILDREN_no
1929+
Attributes:
1930+
- Attribute: DW_AT_name
1931+
Form: DW_FORM_strp
1932+
- Attribute: DW_AT_type
1933+
Form: DW_FORM_ref_addr
1934+
debug_info:
1935+
- Length:
1936+
TotalLength: 22
1937+
Version: 4
1938+
AbbrOffset: 0
1939+
AddrSize: 8
1940+
Entries:
1941+
- AbbrCode: 0x00000001
1942+
Values:
1943+
- Value: 0x0000000000000001
1944+
- AbbrCode: 0x00000002
1945+
Values:
1946+
- Value: 0x000000000000000D
1947+
- Value: 0x0000000000000011
1948+
- AbbrCode: 0x00000000
1949+
Values:
1950+
)";
1951+
auto ErrOrSections = DWARFYAML::EmitDebugSections(StringRef(yamldata));
1952+
ASSERT_TRUE((bool)ErrOrSections);
1953+
1954+
auto &DebugSections = *ErrOrSections;
1955+
1956+
DWARFContextInMemory DwarfContext(DebugSections, 8);
1957+
1958+
std::string str;
1959+
raw_string_ostream strm(str);
1960+
EXPECT_FALSE(DwarfContext.verify(strm, DIDT_All));
1961+
strm.flush();
1962+
const char *err = "error: invalid DIE reference 0x00000011. Offset is in "
1963+
"between DIEs:";
1964+
EXPECT_TRUE(strm.str().find(err) != std::string::npos);
1965+
}
1966+
19111967
} // end anonymous namespace

0 commit comments

Comments
 (0)