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 @@ -168,14 +168,14 @@ // 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) { while (true) { Error Err = Error::success(); StringRef S = DebugLineData.getCStrRef(OffsetPtr, &Err); - if (Err || *OffsetPtr > EndPrologueOffset) { + if (Err) { consumeError(std::move(Err)); return createStringError(errc::invalid_argument, "include directories table was not null " @@ -194,7 +194,7 @@ while (true) { Error Err = Error::success(); StringRef Name = DebugLineData.getCStrRef(OffsetPtr, &Err); - if (!Err && *OffsetPtr <= EndPrologueOffset && Name.empty()) + if (!Err && Name.empty()) break; DWARFDebugLine::FileNameEntry FileEntry; @@ -204,7 +204,7 @@ FileEntry.ModTime = DebugLineData.getULEB128(OffsetPtr, &Err); FileEntry.Length = DebugLineData.getULEB128(OffsetPtr, &Err); - if (Err || *OffsetPtr > EndPrologueOffset) { + if (Err) { consumeError(std::move(Err)); return createStringError( errc::invalid_argument, @@ -344,7 +344,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; @@ -381,6 +381,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); @@ -410,8 +411,8 @@ getVersion() >= 5 ? parseV5DirFileTables(DebugLineData, OffsetPtr, FormParams, Ctx, U, ContentTypes, IncludeDirectories, FileNames) - : parseV2DirFileTables(DebugLineData, OffsetPtr, EndPrologueOffset, - ContentTypes, IncludeDirectories, FileNames); + : parseV2DirFileTables(DebugLineData, OffsetPtr, ContentTypes, + IncludeDirectories, FileNames); if (E) { RecoverableErrorHandler(joinErrors( createStringError( @@ -424,13 +425,14 @@ return Error::success(); } + assert(*OffsetPtr <= EndPrologueOffset); if (*OffsetPtr != EndPrologueOffset) { - RecoverableErrorHandler(createStringError( - errc::invalid_argument, - "parsing line table prologue at 0x%8.8" PRIx64 - " should have ended at 0x%8.8" PRIx64 " but it ended at 0x%8.8" PRIx64, - PrologueOffset, EndPrologueOffset, *OffsetPtr)); - *OffsetPtr = EndPrologueOffset; + RecoverableErrorHandler( + createStringError(errc::invalid_argument, + "unknown data in line table prologue at 0x%8.8" PRIx64 + ": parsing ended (at 0x%8.8" PRIx64 + ") before reaching the prologue at 0x%8.8" PRIx64, + PrologueOffset, *OffsetPtr, EndPrologueOffset)); } return Error::success(); } 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 @@ -192,26 +190,18 @@ .byte -5 # Line Base .byte 14 # Line Range .byte 13 # Opcode Base -.byte 0, 1, 1, 1, 1, 0, 0, 0, 1, 0 # Standard Opcode Lengths +.byte 0, 1, 1, 1, 1, 0, 0, 0, 0, 1 # 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,23 +268,18 @@ # 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 .Linvalid_dir_end0: -# Invalid MD5 hash, where there is data still to be read afterwards. +# Header truncated while reading the MD5 data. .long .Linvalid_md5_end0-.Linvalid_md5_start0 # Length of Unit .Linvalid_md5_start0: .short 5 # DWARF version number @@ -324,7 +306,7 @@ .byte 1 # DW_LNCT_path .byte 0x08 # DW_FORM_string .byte 5 # DW_LNCT_MD5 -.byte 0x0b # DW_FORM_data1 +.byte 0x1e # DW_FORM_data16 .byte 2 # DW_LNCT_directory_index .byte 0x0b # DW_FORM_data1 # File table entries @@ -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 truncated while reading the MD5 form. .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 @@ -123,10 +123,9 @@ ## 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_prologue_end] = 1 +# NONFATAL-NEXT: standard_opcode_lengths[DW_LNS_set_epilogue_begin] = 0 +# NONFATAL-NOT: include_directories # VERBOSE: DW_LNE_end_sequence # VERBOSE: DW_LNS_copy # VERBOSE: DW_LNS_const_add_pc (0x0000000000000011) @@ -148,9 +147,7 @@ # 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 # VERBOSE: DW_LNS_set_epilogue_begin # VERBOSE-NEXT: DW_LNS_copy # VERBOSE: address += 7, line += 4 @@ -163,9 +160,7 @@ ## 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 # VERBOSE: address += 2, line += 1 # VERBOSE: address += 7, line += 0 # VERBOSE: address += 6, line += 7 @@ -187,8 +182,7 @@ # VERBOSE: DW_LNE_set_address (0x1234123412341234) # VERBOSE-NEXT: DW_LNE_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" @@ -259,25 +253,28 @@ # 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 +# ALL-NEXT: warning: unknown data in line table prologue at 0x000000c8: parsing ended (at 0x00000102) before reaching the prologue at 0x00000103 # 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 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 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: unable to decode LEB128 at offset 0x00000219: 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 0x0000029e +# ALL-NEXT: warning: failed to parse file entry because extracting the form value failed. +# 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: unable to decode LEB128 at offset 0x000002d9: 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 0x000003bf +# 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 # 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 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 @@ -423,11 +423,11 @@ Prologue.TotalLength + 1 + Prologue.sizeofTotalLength(); EXPECT_THAT_ERROR( std::move(Recoverable), - FailedWithMessage(("parsing line table prologue at 0x00000000 should " - "have ended at 0x000000" + - Twine::utohexstr(ExpectedEnd) + - " but it ended at 0x000000" + - Twine::utohexstr(ExpectedEnd - 1)) + FailedWithMessage(("unknown data in line table prologue at 0x00000000: " + "parsing ended (at 0x000000" + + Twine::utohexstr(ExpectedEnd - 1) + + ") before reaching the prologue at 0x000000" + + Twine::utohexstr(ExpectedEnd)) .str())); } @@ -450,37 +450,28 @@ 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(); + // The exact place where the parsing will stop depends on the structure of the + // prologue and the last complete field we are able to read. Before V5 we stop + // before reading the file length. In V5, we stop before the filename. + uint64_t ExpectedEnd = Prologue.TotalLength + Prologue.sizeofTotalLength() - + (Version < 5 ? 2 : 8); std::vector Errs; - 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( + (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("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)));