diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp @@ -721,14 +721,17 @@ ProgramLength = BytesRemaining; } + // Create a DataExtractor which can only see the data up to the end of the + // table, to prevent reading past the end. const uint64_t EndOffset = DebugLineOffset + ProgramLength; + DWARFDataExtractor TableData(DebugLineData, EndOffset); // See if we should tell the data extractor the address size. - if (DebugLineData.getAddressSize() == 0) - DebugLineData.setAddressSize(Prologue.getAddressSize()); + if (TableData.getAddressSize() == 0) + TableData.setAddressSize(Prologue.getAddressSize()); else assert(Prologue.getAddressSize() == 0 || - Prologue.getAddressSize() == DebugLineData.getAddressSize()); + Prologue.getAddressSize() == TableData.getAddressSize()); ParsingState State(this, DebugLineOffset, RecoverableErrorHandler); @@ -738,7 +741,7 @@ *OS << format("0x%08.08" PRIx64 ": ", *OffsetPtr); uint64_t OpcodeOffset = *OffsetPtr; - uint8_t Opcode = DebugLineData.getU8(OffsetPtr); + uint8_t Opcode = TableData.getU8(OffsetPtr); if (OS) *OS << format("%02.02" PRIx8 " ", Opcode); @@ -746,7 +749,7 @@ if (Opcode == 0) { // Extended Opcodes always start with a zero opcode followed by // a uleb128 length so you can skip ones you don't know about - uint64_t Len = DebugLineData.getULEB128(OffsetPtr); + uint64_t Len = TableData.getULEB128(OffsetPtr); uint64_t ExtOffset = *OffsetPtr; // Tolerate zero-length; assume length is correct and soldier on. @@ -756,7 +759,7 @@ continue; } - uint8_t SubOpcode = DebugLineData.getU8(OffsetPtr); + uint8_t SubOpcode = TableData.getU8(OffsetPtr); if (OS) *OS << LNExtendedString(SubOpcode); switch (SubOpcode) { @@ -789,7 +792,7 @@ // Make sure the extractor knows the address size. If not, infer it // from the size of the operand. { - uint8_t ExtractorAddressSize = DebugLineData.getAddressSize(); + uint8_t ExtractorAddressSize = TableData.getAddressSize(); uint64_t OpcodeAddressSize = Len - 1; if (ExtractorAddressSize != OpcodeAddressSize && ExtractorAddressSize != 0) @@ -812,13 +815,13 @@ OpcodeAddressSize, ExtOffset)); *OffsetPtr += OpcodeAddressSize; } else { - DebugLineData.setAddressSize(OpcodeAddressSize); - State.Row.Address.Address = DebugLineData.getRelocatedAddress( + TableData.setAddressSize(OpcodeAddressSize); + State.Row.Address.Address = TableData.getRelocatedAddress( OffsetPtr, &State.Row.Address.SectionIndex); // Restore the address size if the extractor already had it. if (ExtractorAddressSize != 0) - DebugLineData.setAddressSize(ExtractorAddressSize); + TableData.setAddressSize(ExtractorAddressSize); } if (OS) @@ -849,12 +852,12 @@ // the file register of the state machine. { FileNameEntry FileEntry; - const char *Name = DebugLineData.getCStr(OffsetPtr); + const char *Name = TableData.getCStr(OffsetPtr); FileEntry.Name = DWARFFormValue::createFromPValue(dwarf::DW_FORM_string, Name); - FileEntry.DirIdx = DebugLineData.getULEB128(OffsetPtr); - FileEntry.ModTime = DebugLineData.getULEB128(OffsetPtr); - FileEntry.Length = DebugLineData.getULEB128(OffsetPtr); + FileEntry.DirIdx = TableData.getULEB128(OffsetPtr); + FileEntry.ModTime = TableData.getULEB128(OffsetPtr); + FileEntry.Length = TableData.getULEB128(OffsetPtr); Prologue.FileNames.push_back(FileEntry); if (OS) *OS << " (" << Name << ", dir=" << FileEntry.DirIdx << ", mod_time=" @@ -864,7 +867,7 @@ break; case DW_LNE_set_discriminator: - State.Row.Discriminator = DebugLineData.getULEB128(OffsetPtr); + State.Row.Discriminator = TableData.getULEB128(OffsetPtr); if (OS) *OS << " (" << State.Row.Discriminator << ")"; break; @@ -913,7 +916,7 @@ // result to the address register of the state machine. { uint64_t AddrOffset = State.advanceAddr( - DebugLineData.getULEB128(OffsetPtr), Opcode, OpcodeOffset); + TableData.getULEB128(OffsetPtr), Opcode, OpcodeOffset); if (OS) *OS << " (" << AddrOffset << ")"; } @@ -922,7 +925,7 @@ case DW_LNS_advance_line: // Takes a single signed LEB128 operand and adds that value to // the line register of the state machine. - State.Row.Line += DebugLineData.getSLEB128(OffsetPtr); + State.Row.Line += TableData.getSLEB128(OffsetPtr); if (OS) *OS << " (" << State.Row.Line << ")"; break; @@ -930,7 +933,7 @@ case DW_LNS_set_file: // Takes a single unsigned LEB128 operand and stores it in the file // register of the state machine. - State.Row.File = DebugLineData.getULEB128(OffsetPtr); + State.Row.File = TableData.getULEB128(OffsetPtr); if (OS) *OS << " (" << State.Row.File << ")"; break; @@ -938,7 +941,7 @@ case DW_LNS_set_column: // Takes a single unsigned LEB128 operand and stores it in the // column register of the state machine. - State.Row.Column = DebugLineData.getULEB128(OffsetPtr); + State.Row.Column = TableData.getULEB128(OffsetPtr); if (OS) *OS << " (" << State.Row.Column << ")"; break; @@ -986,7 +989,7 @@ // requires the use of DW_LNS_advance_pc. Such assemblers, however, // can use DW_LNS_fixed_advance_pc instead, sacrificing compression. { - uint16_t PCOffset = DebugLineData.getRelocatedValue(2, OffsetPtr); + uint16_t PCOffset = TableData.getRelocatedValue(2, OffsetPtr); State.Row.Address.Address += PCOffset; if (OS) *OS @@ -1009,7 +1012,7 @@ case DW_LNS_set_isa: // Takes a single unsigned LEB128 operand and stores it in the // column register of the state machine. - State.Row.Isa = DebugLineData.getULEB128(OffsetPtr); + State.Row.Isa = TableData.getULEB128(OffsetPtr); if (OS) *OS << " (" << (uint64_t)State.Row.Isa << ")"; break; @@ -1022,7 +1025,7 @@ assert(Opcode - 1U < Prologue.StandardOpcodeLengths.size()); uint8_t OpcodeLength = Prologue.StandardOpcodeLengths[Opcode - 1]; for (uint8_t I = 0; I < OpcodeLength; ++I) { - uint64_t Value = DebugLineData.getULEB128(OffsetPtr); + uint64_t Value = TableData.getULEB128(OffsetPtr); if (OS) *OS << format("Skipping ULEB128 value: 0x%16.16" PRIx64 ")\n", Value); diff --git a/llvm/test/tools/llvm-dwarfdump/X86/Inputs/debug_line_malformed.s b/llvm/test/tools/llvm-dwarfdump/X86/Inputs/debug_line_malformed.s --- a/llvm/test/tools/llvm-dwarfdump/X86/Inputs/debug_line_malformed.s +++ b/llvm/test/tools/llvm-dwarfdump/X86/Inputs/debug_line_malformed.s @@ -488,6 +488,36 @@ .byte 0, 1, 1 # DW_LNE_end_sequence .Lunterminated_files_end: +# Opcode extends past the end of the table, as claimed by the unit length field. +.long .Lextended_past_end_end - .Lextended_past_end_start # Length of Unit +.Lextended_past_end_start: +.short 4 # DWARF version number +.long .Lprologue_extended_past_end_end-.Lprologue_extended_past_end_start # Length of Prologue +.Lprologue_extended_past_end_start: +.byte 1 # Minimum Instruction Length +.byte 1 # Maximum Operations per Instruction +.byte 1 # Default is_stmt +.byte -5 # Line Base +.byte 14 # Line Range +.byte 13 # Opcode Base +.byte 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 # Standard Opcode Lengths +.asciz "dir1" # Include table +.asciz "dir2" +.byte 0 +.asciz "file1" # File table +.byte 0, 0, 0 +.asciz "file2" +.byte 1, 0, 0 +.byte 0 +.Lprologue_extended_past_end_end: +.byte 0, 9, 2 # DW_LNE_set_address +.quad 0xfeedfeed +.byte 1 # DW_LNS_copy +.byte 0, 9, 2 # DW_LNE_set_address +.long 0xf001f000 # Truncated address (should be 8 bytes) +.byte 0xf0, 0, 1 +.Lextended_past_end_end: + # Trailing good section. .long .Lunit_good_end - .Lunit_good_start # Length of Unit (DWARF-32 format) .Lunit_good_start: diff --git a/llvm/test/tools/llvm-dwarfdump/X86/debug_line_invalid.test b/llvm/test/tools/llvm-dwarfdump/X86/debug_line_invalid.test --- a/llvm/test/tools/llvm-dwarfdump/X86/debug_line_invalid.test +++ b/llvm/test/tools/llvm-dwarfdump/X86/debug_line_invalid.test @@ -33,7 +33,7 @@ # RUN: FileCheck %s --input-file=%t-malformed-off-first.err --check-prefix=ALL ## Don't stop looking for the later unit if non-fatal issues are found. -# RUN: llvm-dwarfdump -debug-line=0x3c9 %t-malformed.o 2> %t-malformed-off-last.err \ +# RUN: llvm-dwarfdump -debug-line=0x419 %t-malformed.o 2> %t-malformed-off-last.err \ # RUN: | FileCheck %s --check-prefix=LAST --implicit-check-not='debug_line[{{.*}}]' # RUN: FileCheck %s --input-file=%t-malformed-off-last.err --check-prefix=ALL @@ -243,7 +243,14 @@ # VERBOSE: DW_LNE_set_address (0xababcdcdefef0909) # VERBOSE-NEXT: DW_LNE_end_sequence -# LAST: debug_line[0x000003c9] +## Table with extended opcode that overruns table end. +# NONFATAL: debug_line[0x000003c9] +# NONFATAL-NEXT: Line table prologue +# VERBOSE: DW_LNE_set_address (0x00000000feedfeed) +# VERBOSE-NEXT: DW_LNS_copy +# VERBOSE: DW_LNE_set_address (0x0000000000000000) + +# LAST: debug_line[0x00000419] # VERBOSE: DW_LNE_set_address (0x00000000cafebabe) # VERBOSE-NEXT: DW_LNE_end_sequence @@ -272,4 +279,6 @@ # ALL-NEXT: warning: include directories table was not null terminated before the end of the prologue # ALL-NEXT: warning: parsing line table prologue at 0x00000390 found an invalid directory or file table description at 0x000003bf # ALL-NEXT: warning: file names table was not null terminated before the end of the prologue +# OTHER-NEXT: warning: unexpected line op length at offset 0x00000411 expected 0x09 found 0x01 +# OTHER-NEXT: warning: last sequence in debug line table at offset 0x000003c9 is not terminated # ALL-NOT: warning: