]> granicus.if.org Git - llvm/commitdiff
[DWARF] Add more error handling to debug line parser.
authorJonas Devlieghere <jonas@devlieghere.com>
Mon, 22 Jul 2019 23:23:34 +0000 (23:23 +0000)
committerJonas Devlieghere <jonas@devlieghere.com>
Mon, 22 Jul 2019 23:23:34 +0000 (23:23 +0000)
This patch exnteds the error handling in the debug line parser to get
rid of the existing MD5 assertion. I want to reuse the debug line parser
from LLVM in LLDB where we cannot crash on invalid input.

Differential revision: https://reviews.llvm.org/D64544

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@366762 91177308-0d34-0410-b5e6-96231b3b80d8

lib/DebugInfo/DWARF/DWARFDebugLine.cpp
test/tools/llvm-dwarfdump/X86/Inputs/debug_line_malformed.s
test/tools/llvm-dwarfdump/X86/debug_line_invalid.test
unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp

index a1cb1e8582eda22658090655a3fb27660af44dc8..3f9ac13ec7c846049177fd995aba722c728f10b0 100644 (file)
@@ -187,18 +187,24 @@ parseV2DirFileTables(const DWARFDataExtractor &DebugLineData,
 }
 
 // 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<ContentDescriptors>
+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 @@ parseV5EntryFormat(const DWARFDataExtractor &DebugLineData, uint32_t
       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 @@ parseV5DirFileTables(const DWARFDataExtractor &DebugLineData,
                      std::vector<DWARFFormValue> &IncludeDirectories,
                      std::vector<DWARFDebugLine::FileNameEntry> &FileNames) {
   // Get the directory entry description.
-  ContentDescriptors DirDescriptors =
+  llvm::Expected<ContentDescriptors> 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<ContentDescriptors> 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 @@ parseV5DirFileTables(const DWARFDataExtractor &DebugLineData,
         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 @@ parseV5DirFileTables(const DWARFDataExtractor &DebugLineData,
     }
     FileNames.push_back(FileEntry);
   }
-  return true;
+  return Error::success();
 }
 
 Error DWARFDebugLine::Prologue::parse(const DWARFDataExtractor &DebugLineData,
@@ -343,14 +374,17 @@ Error DWARFDebugLine::Prologue::parse(const DWARFDataExtractor &DebugLineData,
   }
 
   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 3fca64f3ead3a2b05708d5fb9307770f6bf0c6e8..5e567a8295abf0257c6a651ff455ce61934a6727 100644 (file)
 .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:
 .quad   0xcafebabe
 .byte   0, 1, 1         # DW_LNE_end_sequence
 .Lunit_good_end:
+
index 29ee24a3f237520c083283480f5fa0902ff183bc..04bf8a7c8360c87749f62a3b68c78dde23b6d4ec 100644 (file)
@@ -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]
 # 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 ce7c252d83ee835ef41b8e5bcfe343dba0c54452..bf2dee4ce2488f7806ce5efa9ec5d0910aa3b02d 100644 (file)
@@ -120,6 +120,16 @@ struct CommonFixture {
     checkError(ExpectedMsg, ExpectedLineTable.takeError());
   }
 
+  void checkGetOrParseLineTableEmitsError(ArrayRef<StringRef> 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<Generator> Gen;
   std::unique_ptr<DWARFContext> Context;
   DWARFDataExtractor LineData;
@@ -344,8 +354,9 @@ TEST_F(DebugLineBasicFixture, ErrorForInvalidV5IncludeDirTable) {
   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) {