Index: llvm/trunk/lib/DebugInfo/DWARF/DWARFDebugLine.cpp =================================================================== --- llvm/trunk/lib/DebugInfo/DWARF/DWARFDebugLine.cpp +++ llvm/trunk/lib/DebugInfo/DWARF/DWARFDebugLine.cpp @@ -187,18 +187,24 @@ } // Parse v5 directory/file entry content descriptions. -// Returns the descriptors, or an empty vector if we did not find a path or -// ran off the end of the prologue. -static ContentDescriptors -parseV5EntryFormat(const DWARFDataExtractor &DebugLineData, uint32_t - *OffsetPtr, uint64_t EndPrologueOffset, DWARFDebugLine::ContentTypeTracker - *ContentTypes) { +// Returns the descriptors, or an error if we did not find a path or ran off +// the end of the prologue. +static llvm::Expected +parseV5EntryFormat(const DWARFDataExtractor &DebugLineData, uint32_t *OffsetPtr, + uint64_t EndPrologueOffset, + DWARFDebugLine::ContentTypeTracker *ContentTypes) { ContentDescriptors Descriptors; int FormatCount = DebugLineData.getU8(OffsetPtr); bool HasPath = false; for (int I = 0; I != FormatCount; ++I) { if (*OffsetPtr >= EndPrologueOffset) - return ContentDescriptors(); + return createStringError( + errc::invalid_argument, + "failed to parse entry content descriptions at offset " + "0x%8.8" PRIx64 + " because offset extends beyond the prologue end at offset " + "0x%8.8" PRIx64, + *OffsetPtr, EndPrologueOffset); ContentDescriptor Descriptor; Descriptor.Type = dwarf::LineNumberEntryFormat(DebugLineData.getULEB128(OffsetPtr)); @@ -209,10 +215,15 @@ ContentTypes->trackContentType(Descriptor.Type); Descriptors.push_back(Descriptor); } - return HasPath ? Descriptors : ContentDescriptors(); + + if (!HasPath) + return createStringError(errc::invalid_argument, + "failed to parse entry content descriptions" + " because no path was found"); + return Descriptors; } -static bool +static Error parseV5DirFileTables(const DWARFDataExtractor &DebugLineData, uint32_t *OffsetPtr, uint64_t EndPrologueOffset, const dwarf::FormParams &FormParams, @@ -221,48 +232,65 @@ std::vector &IncludeDirectories, std::vector &FileNames) { // Get the directory entry description. - ContentDescriptors DirDescriptors = + llvm::Expected DirDescriptors = parseV5EntryFormat(DebugLineData, OffsetPtr, EndPrologueOffset, nullptr); - if (DirDescriptors.empty()) - return false; + if (!DirDescriptors) + return DirDescriptors.takeError(); // Get the directory entries, according to the format described above. int DirEntryCount = DebugLineData.getU8(OffsetPtr); for (int I = 0; I != DirEntryCount; ++I) { if (*OffsetPtr >= EndPrologueOffset) - return false; - for (auto Descriptor : DirDescriptors) { + return createStringError( + errc::invalid_argument, + "failed to parse directory entry at offset " + "0x%8.8" PRIx64 + " because offset extends beyond the prologue end at offset " + "0x%8.8" PRIx64, + *OffsetPtr, EndPrologueOffset); + for (auto Descriptor : *DirDescriptors) { DWARFFormValue Value(Descriptor.Form); switch (Descriptor.Type) { case DW_LNCT_path: if (!Value.extractValue(DebugLineData, OffsetPtr, FormParams, &Ctx, U)) - return false; + return createStringError(errc::invalid_argument, + "failed to parse directory entry because " + "extracting the form value failed."); IncludeDirectories.push_back(Value); break; default: if (!Value.skipValue(DebugLineData, OffsetPtr, FormParams)) - return false; + return createStringError(errc::invalid_argument, + "failed to parse directory entry because " + "skipping the form value failed."); } } } // Get the file entry description. - ContentDescriptors FileDescriptors = - parseV5EntryFormat(DebugLineData, OffsetPtr, EndPrologueOffset, - &ContentTypes); - if (FileDescriptors.empty()) - return false; + llvm::Expected FileDescriptors = parseV5EntryFormat( + DebugLineData, OffsetPtr, EndPrologueOffset, &ContentTypes); + if (!FileDescriptors) + return FileDescriptors.takeError(); // Get the file entries, according to the format described above. int FileEntryCount = DebugLineData.getU8(OffsetPtr); for (int I = 0; I != FileEntryCount; ++I) { if (*OffsetPtr >= EndPrologueOffset) - return false; + return createStringError( + errc::invalid_argument, + "failed to parse file entry at offset " + "0x%8.8" PRIx64 + " because offset extends beyond the prologue end at offset " + "0x%8.8" PRIx64, + *OffsetPtr, EndPrologueOffset); DWARFDebugLine::FileNameEntry FileEntry; - for (auto Descriptor : FileDescriptors) { + for (auto Descriptor : *FileDescriptors) { DWARFFormValue Value(Descriptor.Form); if (!Value.extractValue(DebugLineData, OffsetPtr, FormParams, &Ctx, U)) - return false; + return createStringError(errc::invalid_argument, + "failed to parse file entry because " + "extracting the form value failed."); switch (Descriptor.Type) { case DW_LNCT_path: FileEntry.Name = Value; @@ -280,7 +308,10 @@ FileEntry.Length = Value.getAsUnsignedConstant().getValue(); break; case DW_LNCT_MD5: - assert(Value.getAsBlock().getValue().size() == 16); + if (!Value.getAsBlock() || Value.getAsBlock().getValue().size() != 16) + return createStringError( + errc::invalid_argument, + "failed to parse file entry because the MD5 hash is invalid"); std::uninitialized_copy_n(Value.getAsBlock().getValue().begin(), 16, FileEntry.Checksum.Bytes.begin()); break; @@ -290,7 +321,7 @@ } FileNames.push_back(FileEntry); } - return true; + return Error::success(); } Error DWARFDebugLine::Prologue::parse(const DWARFDataExtractor &DebugLineData, @@ -343,14 +374,17 @@ } if (getVersion() >= 5) { - if (!parseV5DirFileTables(DebugLineData, OffsetPtr, EndPrologueOffset, - FormParams, Ctx, U, ContentTypes, - IncludeDirectories, FileNames)) { - return createStringError(errc::invalid_argument, - "parsing line table prologue at 0x%8.8" PRIx64 - " found an invalid directory or file table description at" - " 0x%8.8" PRIx64, - PrologueOffset, (uint64_t)*OffsetPtr); + if (Error e = parseV5DirFileTables( + DebugLineData, OffsetPtr, EndPrologueOffset, FormParams, Ctx, U, + ContentTypes, IncludeDirectories, FileNames)) { + return joinErrors( + createStringError( + errc::invalid_argument, + "parsing line table prologue at 0x%8.8" PRIx64 + " found an invalid directory or file table description at" + " 0x%8.8" PRIx64, + PrologueOffset, (uint64_t)*OffsetPtr), + std::move(e)); } } else parseV2DirFileTables(DebugLineData, OffsetPtr, EndPrologueOffset, Index: llvm/trunk/test/tools/llvm-dwarfdump/X86/Inputs/debug_line_malformed.s =================================================================== --- llvm/trunk/test/tools/llvm-dwarfdump/X86/Inputs/debug_line_malformed.s +++ llvm/trunk/test/tools/llvm-dwarfdump/X86/Inputs/debug_line_malformed.s @@ -162,6 +162,164 @@ .byte 1 # DW_LNS_copy .Lunit_no_eos_end: +# Invalid prologue length +.long .Linvalid_description_end0-.Linvalid_description_start0 # Length of Unit +.Linvalid_description_start0: +.short 5 # DWARF version number +.byte 8 # Address Size +.byte 0 # Segment Selector Size +.long 15 # Length of Prologue (invalid) +.Linvalid_description_params0: +.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 2 # DW_LNCT_directory_index +.byte 0x0b # DW_FORM_data1 +# File table entries +.byte 1 # 1 file +.asciz "a.c" +.byte 0 +.Linvalid_description_header_end0: +.byte 0,2,4,1 # DW_LNE_set_discriminator 1 +.byte 1 # DW_LNS_copy +.byte 33 # address += 1, line += 1 +.byte 0,1,1 # DW_LNE_end_sequence +.Linvalid_description_end0: + +# Invalid file entry +.long .Linvalid_file_end0-.Linvalid_file_start0 # Length of Unit +.Linvalid_file_start0: +.short 5 # DWARF version number +.byte 8 # Address Size +.byte 0 # Segment Selector Size +.long .Linvalid_file_header_end0-.Linvalid_file_params0-7 # Length of Prologue (invalid) +.Linvalid_file_params0: +.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 2 # DW_LNCT_directory_index +.byte 0x0b # DW_FORM_data1 +# File table entries +.byte 1 # 1 file +.asciz "a.c" +.byte 0 +.Linvalid_file_header_end0: +.byte 0,2,4,1 # DW_LNE_set_discriminator 1 +.byte 1 # DW_LNS_copy +.byte 33 # address += 1, line += 1 +.byte 0,1,1 # DW_LNE_end_sequence +.Linvalid_file_end0: + +# Invalid directory entry +.long .Linvalid_dir_end0-.Linvalid_dir_start0 # Length of Unit +.Linvalid_dir_start0: +.short 5 # DWARF version number +.byte 8 # Address Size +.byte 0 # Segment Selector Size +.long .Linvalid_dir_header_end0-.Linvalid_dir_params0-16 # Length of Prologue (invalid) +.Linvalid_dir_params0: +.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 2 # DW_LNCT_directory_index +.byte 0x0b # DW_FORM_data1 +# File table entries +.byte 1 # 1 file +.asciz "a.c" +.byte 0 +.Linvalid_dir_header_end0: +.byte 0,2,4,1 # DW_LNE_set_discriminator 1 +.byte 1 # DW_LNS_copy +.byte 33 # address += 1, line += 1 +.byte 0,1,1 # DW_LNE_end_sequence +.Linvalid_dir_end0: + +# Invalid MD5 hash +.long .Linvalid_md5_end0-.Linvalid_md5_start0 # Length of Unit +.Linvalid_md5_start0: +.short 5 # DWARF version number +.byte 8 # Address Size +.byte 0 # Segment Selector Size +.long .Linvalid_md5_header_end0-.Linvalid_md5_params0 # Length of Prologue (invalid) +.Linvalid_md5_params0: +.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 3 # 2 elements per file entry +.byte 1 # DW_LNCT_path +.byte 0x08 # DW_FORM_string +.byte 2 # DW_LNCT_directory_index +.byte 0x0b # DW_FORM_data1 +.byte 5 # DW_LNCT_MD5 +.byte 0x09 # DW_FORM_data1 +# File table entries +.byte 1 # 1 file +.asciz "a.c" +.byte 0 +.Linvalid_md5_header_end0: +.byte 0,2,4,1 # DW_LNE_set_discriminator 1 +.byte 1 # DW_LNS_copy +.byte 33 # address += 1, line += 1 +.byte 0,1,1 # DW_LNE_end_sequence +.Linvalid_md5_end0: + # Trailing good section .long .Lunit_good_end - .Lunit_good_start # Length of Unit (DWARF-32 format) .Lunit_good_start: @@ -188,3 +346,4 @@ .quad 0xcafebabe .byte 0, 1, 1 # DW_LNE_end_sequence .Lunit_good_end: + Index: llvm/trunk/test/tools/llvm-dwarfdump/X86/debug_line_invalid.test =================================================================== --- llvm/trunk/test/tools/llvm-dwarfdump/X86/debug_line_invalid.test +++ llvm/trunk/test/tools/llvm-dwarfdump/X86/debug_line_invalid.test @@ -25,7 +25,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=0x183 %t-malformed.o 2> %t-malformed-off-last.err | FileCheck %s --check-prefixes=LASTONLY +# RUN: llvm-dwarfdump -debug-line=0x271 %t-malformed.o 2> %t-malformed-off-last.err | FileCheck %s --check-prefixes=LASTONLY # RUN: FileCheck %s --input-file=%t-malformed-off-last.err --check-prefix=ALL # FIRST: debug_line[0x00000000] @@ -69,23 +69,37 @@ # NONFATAL-NOT: debug_line[{{.*}}] # NONFATAL: 0x00000000deadfade {{.*}} # NONFATAL: debug_line[0x00000183] +# NONFATAL-NEXT: Line table prologue +# NONFATAL: debug_line[0x00000271] # NONFATAL-NOT: debug_line[{{.*}}] # NONFATAL: 0x00000000cafebabe {{.*}} end_sequence # NONFATAL-NOT: debug_line[{{.*}}] # LASTONLY-NOT: debug_line[{{.*}}] -# LASTONLY: debug_line[0x00000183] +# LASTONLY: debug_line[0x00000271] # LASTONLY: 0x00000000cafebabe {{.*}} end_sequence # RESERVED: warning: parsing line table prologue at offset 0x00000048 unsupported reserved unit length found of value 0xfffffffe +# MD5: warning: parsing line table prologue at 0x00000000 found an invalid directory or file table description at 0x0000003b +# MD5-NEXT: warning: failed to parse file entry because the MD5 hash is invalid + # ALL-NOT: warning: # ALL: warning: parsing line table prologue at offset 0x00000048 found unsupported version 0x00 # ALL-NEXT: warning: parsing line table prologue at offset 0x0000004e found unsupported version 0x01 # 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 # FIXME - The latter offset in the next line should be 0xad. The filename parsing code does not notice a missing terminating byte. # ALL-NEXT: warning: parsing line table prologue at 0x00000073 should have ended at 0x000000ab but it ended at 0x000000ac # ALL-NEXT: warning: parsing line table prologue at 0x000000ad should have ended at 0x000000e8 but it ended at 0x000000e7 # OTHER-NEXT: warning: unexpected line op length at offset 0x0000012e expected 0x02 found 0x01 # OTHER-NEXT: warning: last sequence in debug line table is not terminated! +# ALL-NEXT: warning: parsing line table prologue at 0x00000183 found an invalid directory or file table description at 0x000001a2 +# ALL-NEXT: warning: failed to parse entry content descriptions at offset 0x000001a2 because offset extends beyond the prologue end at offset 0x0000019e +# ALL-NEXT: warning: parsing line table prologue at 0x000001be found an invalid directory or file table description at 0x000001eb +# ALL-NEXT: warning: failed to parse file entry at offset 0x000001eb because offset extends beyond the prologue end at offset 0x000001e9 +# ALL-NEXT: warning: parsing line table prologue at 0x000001f9 found an invalid directory or file table description at 0x0000021b +# ALL-NEXT: warning: failed to parse directory entry at offset 0x0000021b because offset extends beyond the prologue end at offset 0x0000021b +# ALL-NEXT: warning: parsing line table prologue at 0x00000234 found an invalid directory or file table description at 0x00000269 +# ALL-NEXT: warning: failed to parse file entry because the MD5 hash is invalid # ALL-NOT: warning: Index: llvm/trunk/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp =================================================================== --- llvm/trunk/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp +++ llvm/trunk/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp @@ -120,6 +120,16 @@ checkError(ExpectedMsg, ExpectedLineTable.takeError()); } + void checkGetOrParseLineTableEmitsError(ArrayRef ExpectedMsgs, + uint64_t Offset = 0) { + auto ExpectedLineTable = Line.getOrParseLineTable( + LineData, Offset, *Context, nullptr, RecordRecoverable); + EXPECT_FALSE(ExpectedLineTable); + EXPECT_FALSE(Recoverable); + + checkError(ExpectedMsgs, ExpectedLineTable.takeError()); + } + std::unique_ptr Gen; std::unique_ptr Context; DWARFDataExtractor LineData; @@ -344,8 +354,9 @@ generate(); checkGetOrParseLineTableEmitsError( - "parsing line table prologue at 0x00000000 found an invalid directory or " - "file table description at 0x00000014"); + {"parsing line table prologue at 0x00000000 found an invalid directory " + "or file table description at 0x00000014", + "failed to parse entry content descriptions because no path was found"}); } TEST_P(DebugLineParameterisedFixture, ErrorForTooLargePrologueLength) {