Skip to content
80 changes: 80 additions & 0 deletions llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -851,6 +851,86 @@ unsigned DWARFVerifier::verifyDebugInfoAttribute(const DWARFDie &Die,
}
break;
}
case DW_AT_LLVM_stmt_sequence: {
// Make sure the offset in the DW_AT_LLVM_stmt_sequence attribute is valid
// and points to a valid sequence offset in the line table.
auto SectionOffset = AttrValue.Value.getAsSectionOffset();
if (!SectionOffset) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add a test for this case

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a test for " invalid DW_AT_LLVM_stmt_sequence encoding"

ReportError("Invalid DW_AT_LLVM_stmt_sequence encoding",
"DIE has invalid DW_AT_LLVM_stmt_sequence encoding");
break;
}
if (*SectionOffset >= U->getLineSection().Data.size()) {
ReportError(
"DW_AT_LLVM_stmt_sequence offset out of bounds",
"DW_AT_LLVM_stmt_sequence offset is beyond .debug_line bounds: " +
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably check if the DW_AT_LLVM_stmt_sequence is inside the current line table only? The .debug_line section conttains multiple line tables, each one has a prologue and then N sequences. We want to make sure the *SectionOffset is after the prologue and before the end of all sequences. Each line table prologue contains:

dwarfdump --debug-line a.out.dSYM -v
a.out.dSYM/Contents/Resources/DWARF/a.out:	file format Mach-O arm64

.debug_line contents:
debug_line[0x00000000]
Line table prologue:
    total_length: 0x00000055
          format: DWARF32
         version: 5
    address_size: 8
 seg_select_size: 0
 prologue_length: 0x00000037

The total_length tells us where the this line table's data ends. And the prologue_length tells us where the prologue ends. So we want to make sure that the *SectionOffset is between the end of the prologue and the and of the current line table, not the entire .debug_line section

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a test for out of range (of the intended line table) test

llvm::formatv("{0:x8}", *SectionOffset));
break;
}

// Get the line table for this unit to validate bounds
const auto *LineTable = DCtx.getLineTableForUnit(U);
if (!LineTable) {
ReportError("DW_AT_LLVM_stmt_sequence without line table",
"DIE has DW_AT_LLVM_stmt_sequence but compile unit has no "
"line table");
break;
}

// Get the DW_AT_stmt_list offset from the compile unit DIE
DWARFDie CUDie = U->getUnitDIE();
auto StmtListOffset = toSectionOffset(CUDie.find(DW_AT_stmt_list));
if (!StmtListOffset) {
ReportError("DW_AT_LLVM_stmt_sequence without DW_AT_stmt_list",
"DIE has DW_AT_LLVM_stmt_sequence but compile unit has no "
"DW_AT_stmt_list");
break;
}

const int8_t DwarfOffset =
LineTable->Prologue.getFormParams().getDwarfOffsetByteSize();
// Calculate the bounds of this specific line table
uint64_t LineTableStart = *StmtListOffset;
uint64_t PrologueLength = LineTable->Prologue.PrologueLength;
uint64_t TotalLength = LineTable->Prologue.TotalLength;
uint64_t LineTableEnd = LineTableStart + TotalLength + DwarfOffset;

// See DWARF definition for this, the following three do not
// count toward prologue length. Calculate SequencesStart correctly
// according to DWARF specification:
uint64_t InitialLengthSize = DwarfOffset;
// Version field is always 2 bytes
uint64_t VersionSize = 2;
uint64_t PrologueLengthSize = DwarfOffset;
uint64_t SequencesStart = LineTableStart + InitialLengthSize + VersionSize +
PrologueLengthSize + PrologueLength;

// Check if the offset is within the bounds of this specific line table
if (*SectionOffset < SequencesStart || *SectionOffset >= LineTableEnd) {
ReportError("DW_AT_LLVM_stmt_sequence offset out of line table bounds",
"DW_AT_LLVM_stmt_sequence offset " +
llvm::formatv("{0:x8}", *SectionOffset) +
" is not within the line table bounds [" +
llvm::formatv("{0:x8}", SequencesStart) + ", " +
llvm::formatv("{0:x8}", LineTableEnd) + ")");
break;
}

// Check if the offset matches any of the sequence offset.
auto It =
std::find_if(LineTable->Sequences.begin(), LineTable->Sequences.end(),
[SectionOffset](const auto &Sequence) {
return Sequence.StmtSeqOffset == *SectionOffset;
});

if (It == LineTable->Sequences.end())
ReportError(
"Invalid DW_AT_LLVM_stmt_sequence offset",
"DW_AT_LLVM_stmt_sequence offset " +
llvm::formatv("{0:x8}", *SectionOffset) +
" does not point to a valid sequence offset in the line table");
break;
}
default:
break;
}
Expand Down
Loading