From 144e4c5a32007bb2fe7b18930dcc4d806ef4f92f Mon Sep 17 00:00:00 2001 From: George Rimar Date: Fri, 27 Oct 2017 10:42:04 +0000 Subject: [PATCH] [llvm-dwarfdump] - Teach verifier to report broken DWARF expressions. Patch improves next things: * Fixes assert/crash in getOpDesc when giving it a invalid expression op code. * DWARFExpression::print() called DWARFExpression::Operation::getEndOffset() which returned and used uninitialized field EndOffset. Patch fixes that. * Teaches verifier to verify DW_AT_location and error out on broken expressions. Differential revision: https://reviews.llvm.org/D39294 llvm-svn: 316756 --- llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp | 10 +++- llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp | 64 +++++++++++++--------- .../llvm-dwarfdump/X86/verify_broken_exprloc.s | 52 ++++++++++++++++++ 3 files changed, 97 insertions(+), 29 deletions(-) create mode 100644 llvm/test/tools/llvm-dwarfdump/X86/verify_broken_exprloc.s diff --git a/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp b/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp index 3417fee..16058e4 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp @@ -104,7 +104,9 @@ static DescVector getDescriptions() { static DWARFExpression::Operation::Description getOpDesc(unsigned OpCode) { // FIXME: Make this constexpr once all compilers are smart enough to do it. static DescVector Descriptions = getDescriptions(); - assert(OpCode < Descriptions.size()); + // Handle possible corrupted or unsupported operation. + if (OpCode >= Descriptions.size()) + return {}; return Descriptions[OpCode]; } @@ -117,8 +119,10 @@ bool DWARFExpression::Operation::extract(DataExtractor Data, uint16_t Version, Opcode = Data.getU8(&Offset); Desc = getOpDesc(Opcode); - if (Desc.Version == Operation::DwarfNA) + if (Desc.Version == Operation::DwarfNA) { + EndOffset = Offset; return false; + } for (unsigned Operand = 0; Operand < 2; ++Operand) { unsigned Size = Desc.Op[Operand]; @@ -221,7 +225,7 @@ bool DWARFExpression::Operation::print(raw_ostream &OS, const MCRegisterInfo *RegInfo, bool isEH) { if (Error) { - OS << "decoding error."; + OS << ""; return false; } diff --git a/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp b/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp index b10697c..f27c849 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp @@ -13,9 +13,11 @@ #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DWARF/DWARFDebugLine.h" #include "llvm/DebugInfo/DWARF/DWARFDie.h" +#include "llvm/DebugInfo/DWARF/DWARFExpression.h" #include "llvm/DebugInfo/DWARF/DWARFFormValue.h" #include "llvm/DebugInfo/DWARF/DWARFSection.h" #include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h" +#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/raw_ostream.h" #include #include @@ -377,45 +379,55 @@ unsigned DWARFVerifier::verifyDieRanges(const DWARFDie &Die, unsigned DWARFVerifier::verifyDebugInfoAttribute(const DWARFDie &Die, DWARFAttribute &AttrValue) { - const DWARFObject &DObj = DCtx.getDWARFObj(); unsigned NumErrors = 0; + auto ReportError = [&](const Twine &TitleMsg) { + ++NumErrors; + error() << TitleMsg << '\n'; + Die.dump(OS, 0, DumpOpts); + OS << "\n"; + }; + + const DWARFObject &DObj = DCtx.getDWARFObj(); const auto Attr = AttrValue.Attr; switch (Attr) { case DW_AT_ranges: // Make sure the offset in the DW_AT_ranges attribute is valid. if (auto SectionOffset = AttrValue.Value.getAsSectionOffset()) { - if (*SectionOffset >= DObj.getRangeSection().Data.size()) { - ++NumErrors; - error() << "DW_AT_ranges offset is beyond .debug_ranges " - "bounds:\n"; - Die.dump(OS, 0, DumpOpts); - OS << "\n"; - } - } else { - ++NumErrors; - error() << "DIE has invalid DW_AT_ranges encoding:\n"; - Die.dump(OS, 0, DumpOpts); - OS << "\n"; + if (*SectionOffset >= DObj.getRangeSection().Data.size()) + ReportError("DW_AT_ranges offset is beyond .debug_ranges bounds:"); + break; } + ReportError("DIE has invalid DW_AT_ranges encoding:"); break; case DW_AT_stmt_list: // Make sure the offset in the DW_AT_stmt_list attribute is valid. if (auto SectionOffset = AttrValue.Value.getAsSectionOffset()) { - if (*SectionOffset >= DObj.getLineSection().Data.size()) { - ++NumErrors; - error() << "DW_AT_stmt_list offset is beyond .debug_line " - "bounds: " - << format("0x%08" PRIx64, *SectionOffset) << "\n"; - Die.dump(OS, 0, DumpOpts); - OS << "\n"; - } - } else { - ++NumErrors; - error() << "DIE has invalid DW_AT_stmt_list encoding:\n"; - Die.dump(OS, 0, DumpOpts); - OS << "\n"; + if (*SectionOffset >= DObj.getLineSection().Data.size()) + ReportError("DW_AT_stmt_list offset is beyond .debug_line bounds: " + + llvm::formatv("{0:x16}", *SectionOffset)); + break; } + ReportError("DIE has invalid DW_AT_stmt_list encoding:"); break; + case DW_AT_location: { + Optional> Expr = AttrValue.Value.getAsBlock(); + if (!Expr) { + ReportError("DIE has invalid DW_AT_location encoding:"); + break; + } + + DWARFUnit *U = Die.getDwarfUnit(); + DataExtractor Data( + StringRef(reinterpret_cast(Expr->data()), Expr->size()), + DCtx.isLittleEndian(), 0); + DWARFExpression Expression(Data, U->getVersion(), U->getAddressByteSize()); + bool Error = llvm::any_of(Expression, [](DWARFExpression::Operation &Op) { + return Op.isError(); + }); + if (Error) + ReportError("DIE contains invalid DWARF expression:"); + break; + } default: break; diff --git a/llvm/test/tools/llvm-dwarfdump/X86/verify_broken_exprloc.s b/llvm/test/tools/llvm-dwarfdump/X86/verify_broken_exprloc.s new file mode 100644 index 0000000..33a9aed --- /dev/null +++ b/llvm/test/tools/llvm-dwarfdump/X86/verify_broken_exprloc.s @@ -0,0 +1,52 @@ +# RUN: llvm-mc %s -filetype obj -triple i686-pc-linux -o %t + +## Check we don't crash when parsing invalid expression opcode. +# RUN: llvm-dwarfdump %t | FileCheck %s +# CHECK: DW_TAG_GNU_call_site_parameter +# CHECK-NEXT: DW_AT_location () + +## Check verifier reports an error. +# RUN: not llvm-dwarfdump -verify %t 2>&1 | FileCheck %s --check-prefix=VERIFY +# VERIFY: DIE contains invalid DWARF expression: +# VERIFY: DW_TAG_GNU_call_site_parameter +# VERIFY-NEXT: DW_AT_location () + +.section .debug_info,"",@progbits + .long 0x12 + .value 0x4 + .long 0 + .byte 0x4 + + .uleb128 0x1 # DW_TAG_compile_unit [1] + .long 0 + .byte 0x0 + + .uleb128 0x2 # DW_TAG_GNU_call_site_parameter [2] + .uleb128 0x1 # Expression size. + .byte 0xff # Broken expression. + + .byte 0 # End mark. + .byte 0 # End mark. + +.section .debug_abbrev,"",@progbits + .uleb128 0x1 # ID [1] + .uleb128 0x11 # DW_TAG_compile_unit, DW_CHILDREN_yes + .byte 0x1 + .uleb128 0x25 # DW_AT_producer, DW_FORM_strp + .uleb128 0xe + .uleb128 0x13 # DW_AT_language, DW_FORM_data1 + .uleb128 0xb + .byte 0 + .byte 0 + + .uleb128 0x2 # ID [2] + .uleb128 0x410a # DW_TAG_GNU_call_site_parameter, DW_CHILDREN_no + .byte 0 + .uleb128 0x2 # DW_AT_location, DW_FORM_exprloc + .uleb128 0x18 + .byte 0 + .byte 0 + .byte 0 + +.section .debug_str,"MS",@progbits,1 +.string "test" -- 2.7.4