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.
This commit is contained in:
David Blaikie 2022-05-05 18:09:34 +00:00
parent 18fd09ab64
commit 0d8cb8b399
5 changed files with 198 additions and 0 deletions

View File

@ -64,6 +64,25 @@ enum DWARFSectionKind {
DW_SECT_EXT_MACINFO = 10,
};
inline const char *toString(DWARFSectionKind Kind) {
switch (Kind) {
case DW_SECT_EXT_unknown:
return "Unknown DW_SECT value 0";
#define STRINGIZE(X) #X
#define HANDLE_DW_SECT(ID, NAME) \
case DW_SECT_##NAME: \
return "DW_SECT_" STRINGIZE(NAME);
#include "llvm/BinaryFormat/Dwarf.def"
case DW_SECT_EXT_TYPES:
return "DW_SECT_TYPES";
case DW_SECT_EXT_LOC:
return "DW_SECT_LOC";
case DW_SECT_EXT_MACINFO:
return "DW_SECT_MACINFO";
}
llvm_unreachable("unknown DWARFSectionKind");
}
/// Convert the internal value for a section kind to an on-disk value.
///
/// The conversion depends on the version of the index section.

View File

@ -14,6 +14,7 @@
#include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h"
#include "llvm/DebugInfo/DWARF/DWARFAddressRange.h"
#include "llvm/DebugInfo/DWARF/DWARFDie.h"
#include "llvm/DebugInfo/DWARF/DWARFUnitIndex.h"
#include <cstdint>
#include <map>
#include <set>
@ -156,6 +157,10 @@ private:
unsigned verifyUnitSection(const DWARFSection &S);
unsigned verifyUnits(const DWARFUnitVector &Units);
unsigned verifyIndexes(const DWARFObject &DObj);
unsigned verifyIndex(StringRef Name, DWARFSectionKind SectionKind,
StringRef Index);
/// Verifies that a call site entry is nested within a subprogram with a
/// DW_AT_call attribute.
///
@ -300,6 +305,24 @@ public:
/// \returns true if all sections verify successfully, false otherwise.
bool handleDebugInfo();
/// Verify the information in the .debug_cu_index section.
///
/// Any errors are reported to the stream that was this object was
/// constructed with.
///
/// \returns true if the .debug_cu_index verifies successfully, false
/// otherwise.
bool handleDebugCUIndex();
/// Verify the information in the .debug_tu_index section.
///
/// Any errors are reported to the stream that was this object was
/// constructed with.
///
/// \returns true if the .debug_tu_index verifies successfully, false
/// otherwise.
bool handleDebugTUIndex();
/// Verify the information in the .debug_line section.
///
/// Any errors are reported to the stream that was this object was

View File

@ -769,6 +769,10 @@ bool DWARFContext::verify(raw_ostream &OS, DIDumpOptions DumpOpts) {
DWARFVerifier verifier(OS, *this, DumpOpts);
Success &= verifier.handleDebugAbbrev();
if (DumpOpts.DumpType & DIDT_DebugCUIndex)
Success &= verifier.handleDebugCUIndex();
if (DumpOpts.DumpType & DIDT_DebugTUIndex)
Success &= verifier.handleDebugTUIndex();
if (DumpOpts.DumpType & DIDT_DebugInfo)
Success &= verifier.handleDebugInfo();
if (DumpOpts.DumpType & DIDT_DebugLine)

View File

@ -6,6 +6,7 @@
//
//===----------------------------------------------------------------------===//
#include "llvm/DebugInfo/DWARF/DWARFVerifier.h"
#include "llvm/ADT/IntervalMap.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/DebugInfo/DWARF/DWARFAbbreviationDeclaration.h"
@ -395,6 +396,57 @@ unsigned DWARFVerifier::verifyUnitSection(const DWARFSection &S) {
return NumDebugInfoErrors;
}
unsigned DWARFVerifier::verifyIndex(StringRef Name,
DWARFSectionKind InfoColumnKind,
StringRef IndexStr) {
if (IndexStr.empty())
return 0;
OS << "Verifying " << Name << "...\n";
DWARFUnitIndex Index(InfoColumnKind);
DataExtractor D(IndexStr, DCtx.isLittleEndian(), 0);
if (!Index.parse(D))
return 1;
IntervalMap<uint32_t, uint64_t>::Allocator Alloc;
std::vector<IntervalMap<uint32_t, uint64_t>> Sections(
Index.getColumnKinds().size(), IntervalMap<uint32_t, uint64_t>(Alloc));
for (const DWARFUnitIndex::Entry &E : Index.getRows()) {
uint64_t Sig = E.getSignature();
if (!E.getContributions())
continue;
for (auto E : enumerate(InfoColumnKind == DW_SECT_INFO
? makeArrayRef(E.getContributions(),
Index.getColumnKinds().size())
: makeArrayRef(E.getContribution(), 1))) {
const DWARFUnitIndex::Entry::SectionContribution &SC = E.value();
int Col = E.index();
if (SC.Length == 0)
continue;
auto &M = Sections[Col];
auto I = M.find(SC.Offset);
if (I != M.end() && I.start() < (SC.Offset + SC.Length)) {
error() << llvm::formatv(
"overlapping index entries for entries {0:x16} "
"and {1:x16} for column {2}\n",
*I, Sig, toString(Index.getColumnKinds()[Col]));
return 1;
}
M.insert(SC.Offset, SC.Offset + SC.Length - 1, Sig);
}
}
return 0;
}
bool DWARFVerifier::handleDebugCUIndex() {
return verifyIndex(".debug_cu_index", DWARFSectionKind::DW_SECT_INFO,
DCtx.getDWARFObj().getCUIndexSection()) == 0;
}
bool DWARFVerifier::handleDebugTUIndex() {
return verifyIndex(".debug_tu_index", DWARFSectionKind::DW_SECT_EXT_TYPES,
DCtx.getDWARFObj().getTUIndexSection()) == 0;
}
bool DWARFVerifier::handleDebugInfo() {
const DWARFObject &DObj = DCtx.getDWARFObj();
unsigned NumErrors = 0;

View File

@ -0,0 +1,100 @@
# RUN: llvm-mc -triple x86_64-unknown-linux %s -filetype=obj -o - | \
# RUN: not llvm-dwarfdump -debug-cu-index -debug-tu-index --verify - | FileCheck %s
# FIXME: The verifier should probably be handled to verify the hash table
# itself - in which case this test would need to be updated to have a correct
# hash table (currently hand crafted with no attempt at correct allocation of
# hashes to buckets) - and probably to verify that the section ranges apply to
# sections that exist, which currently they don't
# This tests that an index that describes units as being in overlapping
# sections is invalid (this was observed in the wild due to overflow due to the
# 32 bit limit of the indexes (a DWARF spec bug - there should be a 64 bit
# version of the index format with 64 bit offsets/sizes)) - but Type Units will
# generally share all the sections other than the info section with each other
# (and with their originating CU) since the dwo format has no way to describe
# which part of non-info-section contributions are used by which units, so
# they're all shared. So demonstrate that the TU index ignores non-info overlap,
# but the CU index diagnoses such overlap (in the abbrev section, in this case)
# This doesn't currently check for info section overlap between the CU and TU
# index, but that could be an extension of this work in the future.
# CHECK: Verifying .debug_cu_index...
# CHECK: error: overlapping index entries for entries 0x0000000000000001 and 0x0000000000000002 for column DW_SECT_ABBREV
# CHECK: Verifying .debug_tu_index...
# CHECK: error: overlapping index entries for entries 0x0000000000000001 and 0x0000000000000003 for column DW_SECT_INFO
.section .debug_cu_index, "", @progbits
## Header:
.long 5 # Version
.long 2 # Section count
.long 3 # Unit count
.long 4 # Slot count
## Hash Table of Signatures:
.quad 0x0000000000000001
.quad 0x0000000000000002
.quad 0x0000000000000003
.quad 0
## Parallel Table of Indexes:
.long 1
.long 2
.long 3
.long 0
## Table of Section Offsets:
## Row 0:
.long 1 # DW_SECT_INFO
.long 3 # DW_SECT_ABBREV
## Row 1:
.long 0x1 # Offset in .debug_info.dwo
.long 0x1 # Offset in .debug_abbrev.dwo
## Row 2:
.long 0x2 # Offset in .debug_info.dwo
.long 0x1 # Offset in .debug_abbrev.dwo
## Row 3:
.long 0x1 # Offset in .debug_info.dwo
.long 0x1 # Offset in .debug_abbrev.dwo
## Table of Section Sizes:
.long 0x1 # Size in .debug_info.dwo
.long 0x1 # Size in .debug_abbrev.dwo
.long 0x1 # Size in .debug_info.dwo
.long 0x1 # Size in .debug_abbrev.dwo
.long 0x1 # Size in .debug_info.dwo
.long 0x1 # Size in .debug_abbrev.dwo
.section .debug_tu_index, "", @progbits
## Header:
.long 5 # Version
.long 2 # Section count
.long 3 # Unit count
.long 4 # Slot count
## Hash Table of Signatures:
.quad 0x0000000000000001
.quad 0x0000000000000002
.quad 0x0000000000000003
.quad 0
## Parallel Table of Indexes:
.long 1
.long 2
.long 3
.long 0
## Table of Section Offsets:
## Row 0:
.long 1 # DW_SECT_INFO
.long 3 # DW_SECT_ABBREV
## Row 1:
.long 0x1 # Offset in .debug_info.dwo
.long 0x1 # Offset in .debug_abbrev.dwo
## Row 2:
.long 0x2 # Offset in .debug_info.dwo
.long 0x1 # Offset in .debug_abbrev.dwo
## Row 3:
.long 0x1 # Offset in .debug_info.dwo
.long 0x1 # Offset in .debug_abbrev.dwo
## Table of Section Sizes:
.long 0x1 # Size in .debug_info.dwo
.long 0x1 # Size in .debug_abbrev.dwo
.long 0x1 # Size in .debug_info.dwo
.long 0x1 # Size in .debug_abbrev.dwo
.long 0x1 # Size in .debug_info.dwo
.long 0x1 # Size in .debug_abbrev.dwo