diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h @@ -129,7 +129,7 @@ void clear(); void dump(raw_ostream &OS, DIDumpOptions DumpOptions) const; - Error parse(const DWARFDataExtractor &DebugLineData, uint64_t *OffsetPtr, + Error parse(DWARFDataExtractor Data, uint64_t *OffsetPtr, function_ref RecoverableErrorHandler, const DWARFContext &Ctx, const DWARFUnit *U = nullptr); }; 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 @@ -165,41 +165,31 @@ // Parse v2-v4 directory and file tables. static Error parseV2DirFileTables(const DWARFDataExtractor &DebugLineData, - uint64_t *OffsetPtr, uint64_t EndPrologueOffset, + uint64_t *OffsetPtr, DWARFDebugLine::ContentTypeTracker &ContentTypes, std::vector &IncludeDirectories, std::vector &FileNames) { - bool Terminated = false; Error Err = Error::success(); while (true) { StringRef S = DebugLineData.getCStrRef(OffsetPtr, &Err); - if (Err || *OffsetPtr > EndPrologueOffset) + if (S.empty()) break; - if (S.empty()) { - Terminated = true; - break; - } DWARFFormValue Dir = DWARFFormValue::createFromPValue(dwarf::DW_FORM_string, S.data()); IncludeDirectories.push_back(Dir); } - if (!Terminated) { + if (Err) { consumeError(std::move(Err)); return createStringError(errc::invalid_argument, "include directories table was not null " "terminated before the end of the prologue"); } - Terminated = false; while (true) { StringRef Name = DebugLineData.getCStrRef(OffsetPtr, &Err); - if (Err || *OffsetPtr > EndPrologueOffset) + if (Name.empty()) break; - if (Name.empty()) { - Terminated = true; - break; - } DWARFDebugLine::FileNameEntry FileEntry; FileEntry.Name = @@ -208,7 +198,7 @@ FileEntry.ModTime = DebugLineData.getULEB128(OffsetPtr, &Err); FileEntry.Length = DebugLineData.getULEB128(OffsetPtr, &Err); - if (Err || *OffsetPtr > EndPrologueOffset) + if (Err) break; FileNames.push_back(FileEntry); } @@ -216,7 +206,7 @@ ContentTypes.HasModTime = true; ContentTypes.HasLength = true; - if (!Terminated) { + if (Err) { consumeError(std::move(Err)); return createStringError(errc::invalid_argument, "file names table was not null terminated before " @@ -353,7 +343,7 @@ } Error DWARFDebugLine::Prologue::parse( - const DWARFDataExtractor &DebugLineData, uint64_t *OffsetPtr, + DWARFDataExtractor DebugLineData, uint64_t *OffsetPtr, function_ref RecoverableErrorHandler, const DWARFContext &Ctx, const DWARFUnit *U) { const uint64_t PrologueOffset = *OffsetPtr; @@ -390,6 +380,7 @@ PrologueLength = DebugLineData.getRelocatedValue(sizeofPrologueLength(), OffsetPtr); const uint64_t EndPrologueOffset = PrologueLength + *OffsetPtr; + DebugLineData = DWARFDataExtractor(DebugLineData, EndPrologueOffset); MinInstLength = DebugLineData.getU8(OffsetPtr); if (getVersion() >= 4) MaxOpsPerInst = DebugLineData.getU8(OffsetPtr); @@ -433,9 +424,9 @@ parseV5DirFileTables(DebugLineData, OffsetPtr, FormParams, Ctx, U, ContentTypes, IncludeDirectories, FileNames)) ReportInvalidDirFileTable(std::move(E)); - } else if (Error E = parseV2DirFileTables(DebugLineData, OffsetPtr, - EndPrologueOffset, ContentTypes, - IncludeDirectories, FileNames)) + } else if (Error E = + parseV2DirFileTables(DebugLineData, OffsetPtr, ContentTypes, + IncludeDirectories, FileNames)) ReportInvalidDirFileTable(std::move(E)); if (*OffsetPtr != EndPrologueOffset) { 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 @@ -81,9 +81,7 @@ .asciz "file2" .byte 1, 2 .Lprologue_short_prologue_end: -.byte 6 # Read as part of the prologue, - # then later again as DW_LNS_negate_stmt. -# Header end +.byte 6 # DW_LNS_negate_stmt. .byte 0, 9, 2 # DW_LNE_set_address .quad 0x1122334455667788 .byte 0, 1, 1 # DW_LNE_end_sequence @@ -194,24 +192,16 @@ .byte 13 # Opcode Base .byte 0, 1, 1, 1, 1, 0, 0, 0, 1, 0 # Standard Opcode Lengths .Linvalid_description_header_end0: -# The bytes from here onwards will also be read as part of the main body. - # --- Prologue interpretation --- | --- Main body interpretation --- -.byte 0, 1 # More standard opcodes | First part of DW_LNE_end_sequence -# Directory table format -.byte 1 # One element per directory entry | End of DW_LNE_end_sequence -.byte 1 # DW_LNCT_path | DW_LNS_copy -.byte 0x08 # DW_FORM_string | DW_LNS_const_add_pc -# Directory table entries -.byte 1 # 1 directory | DW_LNS_copy -.asciz "/tmp" # Directory name | four special opcodes + start of DW_LNE_end_sequence -# File table format -.byte 1 # 1 element per file entry | DW_LNE_end_sequence length -.byte 1 # DW_LNCT_path | DW_LNE_end_sequence opcode -.byte 0x08 # DW_FORM_string | DW_LNS_const_add_pc -# File table entries -.byte 1 # 1 file | DW_LNS_copy -.asciz "xyz" # File name | three special opcodes + start of DW_LNE_set_address -# Header end +.byte 0, 1, 1 # DW_LNE_end_sequence +.byte 1 # DW_LNS_copy +.byte 0x08 # DW_LNS_const_add_pc +.byte 1 # DW_LNS_copy +.asciz "/tmp" # four special opcodes + start of DW_LNE_end_sequence +.byte 1 # DW_LNE_end_sequence length +.byte 1 # DW_LNE_end_sequence opcode +.byte 0x08 # DW_LNS_const_add_pc +.byte 1 # DW_LNS_copy +.asciz "xyz" # three special opcodes + start of DW_LNE_set_address .byte 9, 2 # Remainder of DW_LNE_set_address .quad 0xbabb1ebabb1e .byte 0, 1, 1 # DW_LNE_end_sequence @@ -245,13 +235,10 @@ .byte 0x08 # DW_FORM_string .byte 2 # DW_LNCT_directory_index .Linvalid_file_header_end0: -# The bytes from here onwards will also be read as part of the main body. - # --- Prologue interpretation --- | --- Main body interpretation --- -.byte 0x0b # DW_FORM_data1 | DW_LNS_set_epilogue_begin -# File table entries -.byte 1 # 1 file | DW_LNS_copy -.asciz "xyz" # File name | 3 special opcodes + start of DW_LNE_end_sequence -.byte 1 # Dir index | DW_LNE_end_sequence length +.byte 0x0b # DW_LNS_set_epilogue_begin +.byte 1 # DW_LNS_copy +.asciz "xyz" # 3 special opcodes + start of DW_LNE_end_sequence +.byte 1 # DW_LNE_end_sequence length # Header end .byte 1 # DW_LNE_end_sequence opcode .byte 0, 9, 2 # DW_LNE_set_address @@ -281,17 +268,12 @@ # Directory table entries .byte 1 # 1 directory .Linvalid_dir_header_end0: -# The bytes from here onwards will also be read as part of the main body. - # --- Prologue interpretation --- | --- Main body interpretation --- -.asciz "/tmp" # Directory name | 4 special opcodes + start of DW_LNE_end_sequence -# File table format -.byte 1 # 1 element per file entry | DW_LNE_end_sequence length -.byte 1 # DW_LNCT_path | DW_LNE_end_sequence length opcode -.byte 0x08 # DW_FORM_string | DW_LNS_const_add_pc -# File table entries -.byte 1 # 1 file | DW_LNS_copy -.asciz "xyz" # File name | start of DW_LNE_set_address -# Header end +.asciz "/tmp" # 4 special opcodes + start of DW_LNE_end_sequence +.byte 1 # DW_LNE_end_sequence length +.byte 1 # DW_LNE_end_sequence length opcode +.byte 0x08 # DW_LNS_const_add_pc +.byte 1 # DW_LNS_copy +.asciz "xyz" # start of DW_LNE_set_address .byte 9, 2 # DW_LNE_set_address length + opcode .quad 0x4444333322221111 .byte 0, 1, 1 # DW_LNE_end_sequence @@ -339,8 +321,7 @@ .byte 0, 1, 1 # DW_LNE_end_sequence .Linvalid_md5_end0: -# Invalid MD5 hash, when data beyond the prologue length has -# been read before the MD5 problem is identified. +# Header while reading an md5 hash .long .Linvalid_md5_end1-.Linvalid_md5_start1 # Length of Unit .Linvalid_md5_start1: .short 5 # DWARF version number @@ -368,17 +349,15 @@ .byte 0x08 # DW_FORM_string .byte 5 # DW_LNCT_MD5 .Linvalid_md5_header_end1: -# The bytes from here onwards will also be read as part of the main body. - # --- Prologue interpretation --- | --- Main body interpretation --- -.byte 0x0b # DW_FORM_data1 | DW_LNS_set_epilogue_begin +.byte 0x0b # DW_LNS_set_epilogue_begin # File table entries -.byte 1 # 1 file | DW_LNS_copy -.asciz "xyz" # File name | 3 special opcodes + DW_LNE_set_address start -.byte 9 # MD5 hash value | DW_LNE_set_address length +.byte 1 # DW_LNS_copy +.asciz "xyz" # 3 special opcodes + DW_LNE_set_address start +.byte 9 # DW_LNE_set_address length # Header end -.byte 2 # DW_LNE_set_address opcode +.byte 2 .quad 0x4321432143214321 -.byte 0, 1, 1 # DW_LNE_end_sequence +.byte 0, 1, 1 .Linvalid_md5_end1: # V5 invalid directory content description has unsupported form. 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 @@ -112,10 +112,8 @@ ## Very short prologue length for V5 (ends during parameters). # NONFATAL: debug_line[0x000001b2] # NONFATAL-NEXT: Line table prologue -# NONFATAL: standard_opcode_lengths[DW_LNS_set_isa] = 1 -# NONFATAL-NEXT: include_directories[ 0] = "/tmp" -# NONFATAL-NEXT: file_names[ 0]: -# NONFATAL-NEXT: name: "xyz" +# NONFATAL: standard_opcode_lengths[DW_LNS_set_isa] = 0 +# NONFATAL-NOT: include_directories # NONFATAL: 0x0000000000000000 1 0 1 0 0 is_stmt end_sequence # NONFATAL: 0x0000babb1ebabb1e {{.*}} end_sequence @@ -123,18 +121,14 @@ # NONFATAL: debug_line[0x000001ee] # NONFATAL-NEXT: Line table prologue # NONFATAL: include_directories[ 0] = "/tmp" -# NONFATAL-NEXT: file_names[ 0]: -# NONFATAL-NEXT: name: "xyz" -# NONFATAL-NEXT: dir_index: 1 +# NONFATAL-NOT: file_names # NONFATAL: 0x0000000000000000 {{.*}} epilogue_begin # NONFATAL: 0x00000ab4acadab4a {{.*}} end_sequence ## V5 prologue ends during directory table. # NONFATAL: debug_line[0x0000022f] # NONFATAL-NEXT: Line table prologue -# NONFATAL: include_directories[ 0] = "/tmp" -# NONFATAL-NEXT: file_names[ 0]: -# NONFATAL-NEXT: name: "xyz" +# NONFATAL-NOT: include_directories # NONFATAL: 0x0000000000000002 2 0 1 0 0 is_stmt{{$}} # NONFATAL: 0x4444333322221111 {{.*}} end_sequence @@ -146,8 +140,7 @@ # NONFATAL-NOT: is_stmt # NONFATAL: 0x1234123412341234 {{.*}} end_sequence -## V5 invalid MD5 hash form when data beyond the prologue length has -## been read before the MD5 problem is identified. +## V5 prologue ends while reading an MD5 hash # NONFATAL: debug_line[0x000002ae] # NONFATAL-NEXT: Line table prologue # NONFATAL: include_directories[ 0] = "/tmp" @@ -201,24 +194,27 @@ # ALL-NEXT: warning: parsing line table prologue at offset 0x0000004e: unsupported version 1 # ALL-NEXT: warning: parsing line table prologue at 0x00000054 found an invalid directory or file table description at 0x00000073 # ALL-NEXT: warning: failed to parse entry content descriptions because no path was found -# ALL-NEXT: warning: parsing line table prologue at 0x00000081 found an invalid directory or file table description at 0x000000ba +# ALL-NEXT: warning: parsing line table prologue at 0x00000081 found an invalid directory or file table description at 0x000000b9 # ALL-NEXT: warning: file names table was not null terminated before the end of the prologue # ALL-NEXT: warning: parsing line table prologue at 0x000000c8 should have ended at 0x00000103 but it ended at 0x00000102 # OTHER-NEXT: warning: unexpected line op length at offset 0x00000158 expected 0x02 found 0x01 # 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 0x000001ce but it ended at 0x000001e1 -# ALL-NEXT: warning: parsing line table prologue at 0x000001ee should have ended at 0x00000219 but it ended at 0x00000220 -# ALL-NEXT: warning: parsing line table prologue at 0x0000022f should have ended at 0x00000251 but it ended at 0x0000025e +# ALL-NEXT: warning: parsing line table prologue at 0x000001b2 found an invalid directory or file table description at 0x000001ce +# ALL-NEXT: warning: failed to parse entry content descriptors: unexpected end of data at offset 0x1ce +# ALL-NEXT: warning: parsing line table prologue at 0x000001ee found an invalid directory or file table description at 0x00000219 +# ALL-NEXT: warning: failed to parse entry content descriptors: malformed uleb128, extends past end +# ALL-NEXT: warning: parsing line table prologue at 0x0000022f found an invalid directory or file table description at 0x00000251 +# ALL-NEXT: warning: failed to parse directory entry because extracting the form value failed. # ALL-NEXT: warning: parsing line table prologue at 0x0000026b found an invalid directory or file table description at 0x0000029f # ALL-NEXT: warning: failed to parse file entry because the MD5 hash is invalid -# ALL-NEXT: warning: parsing line table prologue at 0x000002ae found an invalid directory or file table description at 0x000002e0 -# ALL-NEXT: warning: failed to parse file entry because the MD5 hash is invalid +# ALL-NEXT: warning: parsing line table prologue at 0x000002ae found an invalid directory or file table description at 0x000002d9 +# ALL-NEXT: warning: failed to parse entry content descriptors: malformed uleb128, extends past end # ALL-NEXT: warning: parsing line table prologue at 0x000002ec found an invalid directory or file table description at 0x00000315 # ALL-NEXT: warning: failed to parse directory entry because skipping the form value failed. # ALL-NEXT: warning: parsing line table prologue at offset 0x00000332 found opcode base of 0. Assuming no standard opcodes -# ALL-NEXT: warning: parsing line table prologue at 0x00000361 found an invalid directory or file table description at 0x00000383 +# ALL-NEXT: warning: parsing line table prologue at 0x00000361 found an invalid directory or file table description at 0x00000382 # 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 0x000003bc +# ALL-NEXT: warning: parsing line table prologue at 0x00000390 found an invalid directory or file table description at 0x000003bb # ALL-NEXT: warning: file names table was not null terminated before the end of the prologue # ALL-NOT: warning: diff --git a/llvm/test/tools/llvm-dwarfdump/X86/debug_line_short_prologue.s b/llvm/test/tools/llvm-dwarfdump/X86/debug_line_short_prologue.s deleted file mode 100644 --- a/llvm/test/tools/llvm-dwarfdump/X86/debug_line_short_prologue.s +++ /dev/null @@ -1,37 +0,0 @@ -# RUN: llvm-mc -triple x86_64-pc-linux %s -filetype=obj -o %t -# RUN: llvm-dwarfdump -debug-line %t 2>&1 | FileCheck %s - -# CHECK: debug_line[0x00000000] -# CHECK-NEXT: warning: parsing line table prologue at 0x00000000 found an invalid directory or file table description at 0x0000002b -# CHECK-NEXT: warning: failed to parse entry content descriptors: malformed uleb128, extends past end -# CHECK: include_directories[ 0] = "/tmp" - -.section .debug_line,"",@progbits -.long .Lshort_prologue_end1-.Lshort_prologue_start1 # Length of Unit -.Lshort_prologue_start1: -.short 5 # DWARF version number -.byte 8 # Address Size -.byte 0 # Segment Selector Size -.long .Lshort_prologue_header_end1 - .Lshort_prologue_params1 # Length of Prologue -.Lshort_prologue_params1: -.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 -# Directory table format -.byte 1 # One element per directory entry -.byte 1 # DW_LNCT_path -.byte 0x08 # DW_FORM_string -# Directory table entries -.byte 1 # 1 directory -.asciz "/tmp" -# File table format -.byte 2 # 2 elements per file entry -.byte 1 # DW_LNCT_path -.byte 0x08 # DW_FORM_string -.byte 5 # DW_LNCT_MD5 -.Lshort_prologue_header_end1: -.Lshort_prologue_end1: diff --git a/llvm/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp --- a/llvm/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp +++ b/llvm/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp @@ -449,37 +449,25 @@ ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded()); DWARFDebugLine::LineTable Result(**ExpectedLineTable); - if (Version != 5) { - // Parsing will stop before reading a complete file entry. - ASSERT_EQ(Result.Prologue.IncludeDirectories.size(), 1u); - EXPECT_EQ(toStringRef(Result.Prologue.IncludeDirectories[0]), "a dir"); - EXPECT_EQ(Result.Prologue.FileNames.size(), 0u); - } else { - // Parsing will continue past the presumed end of prologue. - ASSERT_EQ(Result.Prologue.FileNames.size(), 1u); - ASSERT_EQ(Result.Prologue.FileNames[0].Name.getForm(), DW_FORM_string); - ASSERT_EQ(Result.Prologue.FileNames[0].DirIdx, 0u); - EXPECT_EQ(toStringRef(Result.Prologue.FileNames[0].Name), "a file"); - } + // Parsing will stop before reading a complete file entry. + ASSERT_EQ(Result.Prologue.IncludeDirectories.size(), 1u); + EXPECT_EQ(toStringRef(Result.Prologue.IncludeDirectories[0]), "a dir"); + EXPECT_EQ(Result.Prologue.FileNames.size(), 0u); - uint64_t ExpectedEnd = - Prologue.TotalLength - 2 + Prologue.sizeofTotalLength(); + uint64_t ExpectedEnd = Prologue.TotalLength + Prologue.sizeofTotalLength() - + (Version != 5 ? 2 : 8); std::vector Errs; + Errs.emplace_back( + (Twine("parsing line table prologue at 0x00000000 found an invalid " + "directory or file table description at 0x000000") + + Twine::utohexstr(ExpectedEnd)) + .str()); if (Version != 5) { - Errs.emplace_back( - (Twine("parsing line table prologue at 0x00000000 found an invalid " - "directory or file table description at 0x000000") + - Twine::utohexstr(ExpectedEnd + 1)) - .str()); Errs.emplace_back("file names table was not null terminated before the end " "of the prologue"); } else { Errs.emplace_back( - (Twine("parsing line table prologue at 0x00000000 should have ended at " - "0x000000") + - Twine::utohexstr(ExpectedEnd) + " but it ended at 0x000000" + - Twine::utohexstr(ExpectedEnd + 2)) - .str()); + "failed to parse file entry because extracting the form value failed."); } EXPECT_THAT_ERROR(std::move(Recoverable), FailedWithMessageArray(testing::ElementsAreArray(Errs)));