forked from OSchip/llvm-project
[DebugInfo] Make incorrect debug line extended opcode length non-fatal
It is possible to try to keep parsing a debug line program even when the length of an extended opcode does not match what is expected for that opcode. This patch changes what was previously a fatal error to be non-fatal. The parser now continues by assuming the the claimed length is correct, even if it means moving the offset backwards. Reviewed by: dblaikie Differential Revision: https://reviews.llvm.org/D72155
This commit is contained in:
parent
ac0b9b4ccf
commit
f1be770ff6
|
@ -685,13 +685,18 @@ Error DWARFDebugLine::LineTable::parse(
|
|||
(*OffsetPtr) += Len - 1;
|
||||
break;
|
||||
}
|
||||
// Make sure the stated and parsed lengths are the same.
|
||||
// Otherwise we have an unparseable line-number program.
|
||||
if (*OffsetPtr - ExtOffset != Len)
|
||||
return createStringError(errc::illegal_byte_sequence,
|
||||
"unexpected line op length at offset 0x%8.8" PRIx64
|
||||
" expected 0x%2.2" PRIx64 " found 0x%2.2" PRIx64,
|
||||
ExtOffset, Len, *OffsetPtr - ExtOffset);
|
||||
// Make sure the length as recorded in the table and the standard length
|
||||
// for the opcode match. If they don't, continue from the end as claimed
|
||||
// by the table.
|
||||
uint64_t End = ExtOffset + Len;
|
||||
if (*OffsetPtr != End) {
|
||||
RecoverableErrorCallback(createStringError(
|
||||
errc::illegal_byte_sequence,
|
||||
"unexpected line op length at offset 0x%8.8" PRIx64
|
||||
" expected 0x%2.2" PRIx64 " found 0x%2.2" PRIx64,
|
||||
ExtOffset, Len, *OffsetPtr - ExtOffset));
|
||||
*OffsetPtr = End;
|
||||
}
|
||||
} else if (Opcode < Prologue.OpcodeBase) {
|
||||
if (OS)
|
||||
*OS << LNStandardString(Opcode);
|
||||
|
|
|
@ -114,7 +114,7 @@
|
|||
.byte 0, 1, 1 # DW_LNE_end_sequence
|
||||
.Lunit_long_prologue_end:
|
||||
|
||||
# Over-long extended opcode.
|
||||
# Incorrect length extended opcodes.
|
||||
.long .Lunit_long_opcode_end - .Lunit_long_opcode_start # unit length
|
||||
.Lunit_long_opcode_start:
|
||||
.short 4 # version
|
||||
|
@ -139,6 +139,9 @@
|
|||
.byte 0, 9, 2 # DW_LNE_set_address
|
||||
.quad 0xabbadaba
|
||||
.byte 0, 2, 1 # DW_LNE_end_sequence (too long)
|
||||
.byte 6 # DW_LNS_negate_stmt (but will be consumed with the end sequence above).
|
||||
.byte 0, 1, 4 # DW_LNE_set_discriminator (too short)
|
||||
.byte 0xa # Parsed as argument for set_discriminator and also DW_LNS_set_prologue_end.
|
||||
.byte 0, 9, 2 # DW_LNE_set_address
|
||||
.quad 0xbabb1e45
|
||||
.byte 0, 1, 1 # DW_LNE_end_sequence
|
||||
|
|
|
@ -36,7 +36,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=0x2af %t-malformed.o 2> %t-malformed-off-last.err \
|
||||
# RUN: llvm-dwarfdump -debug-line=0x2b4 %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
|
||||
|
||||
|
@ -96,21 +96,18 @@
|
|||
|
||||
## Case 6: Extended opcode with incorrect length versus expected.
|
||||
# NONFATAL: debug_line[0x00000111]
|
||||
## Dumping prints the line table prologue and any valid operations up to the
|
||||
## point causing the problem.
|
||||
# NONFATAL-NEXT: Line table prologue
|
||||
# NONFATAL: 0x00000000abbadaba {{.*}} end_sequence
|
||||
# NONFATAL-NOT: is_stmt
|
||||
# NONFATAL: 0x00000000abbadaba {{.*}} end_sequence
|
||||
# NONFATAL: 0x00000000babb1e45 {{.*}} 10 is_stmt prologue_end end_sequence{{$}}
|
||||
|
||||
## For minor issues, we can dump the whole table.
|
||||
## Case 7: No end of sequence.
|
||||
# NONFATAL: debug_line[0x00000167]
|
||||
# NONFATAL: debug_line[0x0000016c]
|
||||
# NONFATAL-NEXT: Line table prologue
|
||||
# NONFATAL: 0x00000000deadfade {{.*}} is_stmt
|
||||
# NONFATAL-NOT: end_sequence
|
||||
|
||||
## Case 8: Very short prologue length for V5 (ends during parameters).
|
||||
# NONFATAL: debug_line[0x000001ad]
|
||||
# NONFATAL: debug_line[0x000001b2]
|
||||
# NONFATAL-NEXT: Line table prologue
|
||||
# NONFATAL: standard_opcode_lengths[DW_LNS_set_isa] = 1
|
||||
# NONFATAL-NEXT: include_directories[ 0] = "/tmp"
|
||||
|
@ -120,7 +117,7 @@
|
|||
# NONFATAL-NOT: Address
|
||||
|
||||
## Case 9: V5 prologue ends during file table.
|
||||
# NONFATAL: debug_line[0x000001ed]
|
||||
# NONFATAL: debug_line[0x000001f2]
|
||||
# NONFATAL-NEXT: Line table prologue
|
||||
# NONFATAL: include_directories[ 0] = "/tmp"
|
||||
# NONFATAL-NEXT: file_names[ 0]:
|
||||
|
@ -129,7 +126,7 @@
|
|||
# NONFATAL-NOT: Address
|
||||
|
||||
## Case 10: V5 prologue ends during directory table.
|
||||
# NONFATAL: debug_line[0x0000022d]
|
||||
# NONFATAL: debug_line[0x00000232]
|
||||
# NONFATAL-NEXT: Line table prologue
|
||||
# NONFATAL: include_directories[ 0] = "/tmp"
|
||||
# NONFATAL-NEXT: file_names[ 0]:
|
||||
|
@ -138,13 +135,13 @@
|
|||
# NONFATAL-NOT: Address
|
||||
|
||||
## Case 11: V5 invalid MD5 hash form.
|
||||
# NONFATAL: debug_line[0x0000026d]
|
||||
# NONFATAL: debug_line[0x00000272]
|
||||
# NONFATAL-NEXT: Line table prologue
|
||||
# NONFATAL: include_directories[ 0] = "/tmp"
|
||||
# NONFATAL-NOT: file_names
|
||||
# NONFATAL-NOT: Address
|
||||
|
||||
# LAST: debug_line[0x000002af]
|
||||
# LAST: debug_line[0x000002b4]
|
||||
# LAST: 0x00000000cafebabe {{.*}} end_sequence
|
||||
|
||||
# RESERVED: warning: parsing line table prologue at offset 0x00000048 unsupported reserved unit length found of value 0xfffffffe
|
||||
|
@ -158,10 +155,11 @@
|
|||
# ALL-NEXT: warning: parsing line table prologue at 0x00000081 should have ended at 0x000000b9 but it ended at 0x000000ba
|
||||
# ALL-NEXT: warning: parsing line table prologue at 0x000000c9 should have ended at 0x00000104 but it ended at 0x00000103
|
||||
# OTHER-NEXT: warning: unexpected line op length at offset 0x00000158 expected 0x02 found 0x01
|
||||
# OTHER-NEXT: warning: last sequence in debug line table at offset 0x00000167 is not terminated
|
||||
# ALL-NEXT: warning: parsing line table prologue at 0x000001ad should have ended at 0x000001c8 but it ended at 0x000001df
|
||||
# ALL-NEXT: warning: parsing line table prologue at 0x000001ed should have ended at 0x00000218 but it ended at 0x0000021f
|
||||
# ALL-NEXT: warning: parsing line table prologue at 0x0000022d should have ended at 0x0000024f but it ended at 0x0000025f
|
||||
# ALL-NEXT: warning: parsing line table prologue at 0x0000026d found an invalid directory or file table description at 0x000002a2
|
||||
# OTHER-NEXT: warning: unexpected line op length at offset 0x0000015c expected 0x01 found 0x02
|
||||
# OTHER-NEXT: warning: last sequence in debug line table at offset 0x0000016c is not terminated
|
||||
# ALL-NEXT: warning: parsing line table prologue at 0x000001b2 should have ended at 0x000001cd but it ended at 0x000001e4
|
||||
# ALL-NEXT: warning: parsing line table prologue at 0x000001f2 should have ended at 0x0000021d but it ended at 0x00000224
|
||||
# ALL-NEXT: warning: parsing line table prologue at 0x00000232 should have ended at 0x00000254 but it ended at 0x00000264
|
||||
# ALL-NEXT: warning: parsing line table prologue at 0x00000272 found an invalid directory or file table description at 0x000002a7
|
||||
# ALL-NEXT: warning: failed to parse file entry because the MD5 hash is invalid
|
||||
# ALL-NOT: warning:
|
||||
|
|
|
@ -428,19 +428,62 @@ INSTANTIATE_TEST_CASE_P(
|
|||
std::make_pair(4, DWARF64), // Test v4 fields and DWARF64.
|
||||
std::make_pair(5, DWARF32), std::make_pair(5, DWARF64)), );
|
||||
|
||||
TEST_F(DebugLineBasicFixture, ErrorForInvalidExtendedOpcodeLength) {
|
||||
TEST_F(DebugLineBasicFixture, ErrorForExtendedOpcodeLengthSmallerThanExpected) {
|
||||
if (!setupGenerator())
|
||||
return;
|
||||
|
||||
LineTable < = Gen->addLineTable();
|
||||
// The Length should be 1 for an end sequence opcode.
|
||||
LT.addExtendedOpcode(2, DW_LNE_end_sequence, {});
|
||||
LT.addByte(0xaa);
|
||||
// The Length should be 1 + sizeof(ULEB) for a set discriminator opcode.
|
||||
// The operand will be read for both the discriminator opcode and then parsed
|
||||
// again as DW_LNS_negate_stmt, to respect the claimed length.
|
||||
LT.addExtendedOpcode(1, DW_LNE_set_discriminator,
|
||||
{{DW_LNS_negate_stmt, LineTable::ULEB}});
|
||||
LT.addByte(0xbb);
|
||||
LT.addStandardOpcode(DW_LNS_const_add_pc, {});
|
||||
LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
|
||||
|
||||
generate();
|
||||
|
||||
checkGetOrParseLineTableEmitsFatalError(
|
||||
"unexpected line op length at offset "
|
||||
"0x00000030 expected 0x02 found 0x01");
|
||||
auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,
|
||||
nullptr, RecordRecoverable);
|
||||
checkError(
|
||||
"unexpected line op length at offset 0x00000031 expected 0x01 found 0x02",
|
||||
std::move(Recoverable));
|
||||
ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());
|
||||
ASSERT_EQ((*ExpectedLineTable)->Rows.size(), 3u);
|
||||
EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 1u);
|
||||
EXPECT_EQ((*ExpectedLineTable)->Rows[1].IsStmt, 0u);
|
||||
EXPECT_EQ((*ExpectedLineTable)->Rows[1].Discriminator, DW_LNS_negate_stmt);
|
||||
}
|
||||
|
||||
TEST_F(DebugLineBasicFixture, ErrorForExtendedOpcodeLengthLargerThanExpected) {
|
||||
if (!setupGenerator())
|
||||
return;
|
||||
|
||||
LineTable < = Gen->addLineTable();
|
||||
LT.addByte(0xaa);
|
||||
LT.addStandardOpcode(DW_LNS_const_add_pc, {});
|
||||
// The Length should be 1 for an end sequence opcode.
|
||||
LT.addExtendedOpcode(2, DW_LNE_end_sequence, {});
|
||||
// The negate statement opcode will be skipped.
|
||||
LT.addStandardOpcode(DW_LNS_negate_stmt, {});
|
||||
LT.addByte(0xbb);
|
||||
LT.addStandardOpcode(DW_LNS_const_add_pc, {});
|
||||
LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
|
||||
|
||||
generate();
|
||||
|
||||
auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,
|
||||
nullptr, RecordRecoverable);
|
||||
checkError(
|
||||
"unexpected line op length at offset 0x00000032 expected 0x02 found 0x01",
|
||||
std::move(Recoverable));
|
||||
ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());
|
||||
ASSERT_EQ((*ExpectedLineTable)->Rows.size(), 4u);
|
||||
EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 2u);
|
||||
ASSERT_EQ((*ExpectedLineTable)->Sequences[1].FirstRowIndex, 2u);
|
||||
EXPECT_EQ((*ExpectedLineTable)->Rows[2].IsStmt, 1u);
|
||||
}
|
||||
|
||||
TEST_F(DebugLineBasicFixture, ErrorForUnitLengthTooLarge) {
|
||||
|
@ -695,11 +738,11 @@ TEST_F(DebugLineBasicFixture, ParserReportsNonPrologueProblemsWhenParsing) {
|
|||
|
||||
DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs);
|
||||
Parser.parseNext(RecordRecoverable, RecordUnrecoverable);
|
||||
EXPECT_FALSE(Recoverable);
|
||||
EXPECT_FALSE(Unrecoverable);
|
||||
ASSERT_FALSE(Parser.done());
|
||||
checkError(
|
||||
"unexpected line op length at offset 0x00000030 expected 0x42 found 0x01",
|
||||
std::move(Unrecoverable));
|
||||
std::move(Recoverable));
|
||||
|
||||
// Reset the error state so that it does not confuse the next set of checks.
|
||||
Unrecoverable = Error::success();
|
||||
|
|
Loading…
Reference in New Issue