Skip to content

[llvm-debuginfo-analyzer] Fix a couple of unhandled DWARF situations leading to a crash #137221

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

jalopezg-git
Copy link
Contributor

This pull request fixes a couple of unhandled situations in DWARF input leading to a crash. Specifically,

  • If the DWARF input contains a declaration of a C variadic function (where ... translates to DW_TAG_unspecified_parameters), which is then followed by a definition, llvm_unreachable() is hit in LVScope::addMissingElements().
    This is only visible in Debug builds (see stack trace below), but still. test-dwarf-clang-unspec-params.elf triggers this condition.
Invalid symbol kind.
UNREACHABLE executed at /home/jalopezg/repos/llvm-project/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp:345!
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
Stack dump:
0.	Program arguments: _build/Debug/bin/llvm-debuginfo-analyzer --print=all --attribute=all /tmp/test-dwarf-clang-unspec-params.elf
 #0 0x00005577295666f6 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) /home/jalopezg/repos/llvm-project/llvm/lib/Support/Unix/Signals.inc:804:22
 #1 0x0000557729566b09 PrintStackTraceSignalHandler(void*) /home/jalopezg/repos/llvm-project/llvm/lib/Support/Unix/Signals.inc:880:1
 #2 0x0000557729563f1f llvm::sys::RunSignalHandlers() /home/jalopezg/repos/llvm-project/llvm/lib/Support/Signals.cpp:105:20
 #3 0x0000557729565fb4 SignalHandler(int, siginfo_t*, void*) /home/jalopezg/repos/llvm-project/llvm/lib/Support/Unix/Signals.inc:418:13
 #4 0x00007fc5ff23e710 (/usr/lib/libc.so.6+0x3e710)
 #5 0x00007fc5ff28e83c (/usr/lib/libc.so.6+0x8e83c)
 #6 0x00007fc5ff23e668 gsignal (/usr/lib/libc.so.6+0x3e668)
 #7 0x00007fc5ff2264b8 abort (/usr/lib/libc.so.6+0x264b8)
 #8 0x00005577294ad073 bindingsErrorHandler(void*, char const*, bool) /home/jalopezg/repos/llvm-project/llvm/lib/Support/ErrorHandling.cpp:223:55
 #9 0x0000557728f56b0c llvm::logicalview::LVScope::addMissingElements(llvm::logicalview::LVScope*) /home/jalopezg/repos/llvm-project/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp:322:5
#10 0x0000557728f5f4b7 llvm::logicalview::LVScopeFunction::resolveReferences() /home/jalopezg/repos/llvm-project/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp:1773:9
  • Parsing of instructions in LVBinaryReader::createInstructions() does not check whether Offset lies within the Bytes ArrayRef. A specially crafted DWARF input can lead to this condition.

FYI, @CarlosAlbertoEnciso. I believe this patchset is ready; feel free to start review.

@llvmbot
Copy link
Member

llvmbot commented Apr 24, 2025

@llvm/pr-subscribers-llvm-binary-utilities

@llvm/pr-subscribers-debuginfo

Author: Javier Lopez-Gomez (jalopezg-git)

Changes

This pull request fixes a couple of unhandled situations in DWARF input leading to a crash. Specifically,

  • If the DWARF input contains a declaration of a C variadic function (where ... translates to DW_TAG_unspecified_parameters), which is then followed by a definition, llvm_unreachable() is hit in LVScope::addMissingElements().
    This is only visible in Debug builds (see stack trace below), but still. test-dwarf-clang-unspec-params.elf triggers this condition.
Invalid symbol kind.
UNREACHABLE executed at /home/jalopezg/repos/llvm-project/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp:345!
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
Stack dump:
0.	Program arguments: _build/Debug/bin/llvm-debuginfo-analyzer --print=all --attribute=all /tmp/test-dwarf-clang-unspec-params.elf
 #<!-- -->0 0x00005577295666f6 llvm::sys::PrintStackTrace(llvm::raw_ostream&amp;, int) /home/jalopezg/repos/llvm-project/llvm/lib/Support/Unix/Signals.inc:804:22
 #<!-- -->1 0x0000557729566b09 PrintStackTraceSignalHandler(void*) /home/jalopezg/repos/llvm-project/llvm/lib/Support/Unix/Signals.inc:880:1
 #<!-- -->2 0x0000557729563f1f llvm::sys::RunSignalHandlers() /home/jalopezg/repos/llvm-project/llvm/lib/Support/Signals.cpp:105:20
 #<!-- -->3 0x0000557729565fb4 SignalHandler(int, siginfo_t*, void*) /home/jalopezg/repos/llvm-project/llvm/lib/Support/Unix/Signals.inc:418:13
 #<!-- -->4 0x00007fc5ff23e710 (/usr/lib/libc.so.6+0x3e710)
 #<!-- -->5 0x00007fc5ff28e83c (/usr/lib/libc.so.6+0x8e83c)
 #<!-- -->6 0x00007fc5ff23e668 gsignal (/usr/lib/libc.so.6+0x3e668)
 #<!-- -->7 0x00007fc5ff2264b8 abort (/usr/lib/libc.so.6+0x264b8)
 #<!-- -->8 0x00005577294ad073 bindingsErrorHandler(void*, char const*, bool) /home/jalopezg/repos/llvm-project/llvm/lib/Support/ErrorHandling.cpp:223:55
 #<!-- -->9 0x0000557728f56b0c llvm::logicalview::LVScope::addMissingElements(llvm::logicalview::LVScope*) /home/jalopezg/repos/llvm-project/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp:322:5
#<!-- -->10 0x0000557728f5f4b7 llvm::logicalview::LVScopeFunction::resolveReferences() /home/jalopezg/repos/llvm-project/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp:1773:9
  • Parsing of instructions in LVBinaryReader::createInstructions() does not check whether Offset lies within the Bytes ArrayRef. A specially crafted DWARF input can lead to this condition.

FYI, @CarlosAlbertoEnciso. I believe this patchset is ready; feel free to start review.


Full diff: https://github.com/llvm/llvm-project/pull/137221.diff

4 Files Affected:

  • (modified) llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp (+4-1)
  • (modified) llvm/lib/DebugInfo/LogicalView/Readers/LVBinaryReader.cpp (+7)
  • (modified) llvm/unittests/DebugInfo/LogicalView/DWARFReaderTest.cpp (+32-1)
  • (added) llvm/unittests/DebugInfo/LogicalView/Inputs/test-dwarf-clang-unspec-params.elf ()
diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp
index 8bbaf93db0caa..f187b1a57bd45 100644
--- a/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp
+++ b/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp
@@ -330,13 +330,16 @@ void LVScope::addMissingElements(LVScope *Reference) {
       Symbol->setIsOptimized();
       Symbol->setReference(Reference);
 
-      // The symbol can be a constant, parameter or variable.
+      // The symbol can be a constant, parameter, variable or unspecified
+      // parameters (i.e. `...`).
       if (Reference->getIsConstant())
         Symbol->setIsConstant();
       else if (Reference->getIsParameter())
         Symbol->setIsParameter();
       else if (Reference->getIsVariable())
         Symbol->setIsVariable();
+      else if (Reference->getIsUnspecified())
+        Symbol->setIsUnspecified();
       else
         llvm_unreachable("Invalid symbol kind.");
     }
diff --git a/llvm/lib/DebugInfo/LogicalView/Readers/LVBinaryReader.cpp b/llvm/lib/DebugInfo/LogicalView/Readers/LVBinaryReader.cpp
index ad14baa0c9269..b12208c53b8e3 100644
--- a/llvm/lib/DebugInfo/LogicalView/Readers/LVBinaryReader.cpp
+++ b/llvm/lib/DebugInfo/LogicalView/Readers/LVBinaryReader.cpp
@@ -433,6 +433,13 @@ Error LVBinaryReader::createInstructions(LVScope *Scope,
 
   ArrayRef<uint8_t> Bytes = arrayRefFromStringRef(*SectionContentsOrErr);
   uint64_t Offset = Address - SectionAddress;
+  if (Offset > Bytes.size()) {
+    LLVM_DEBUG({
+      dbgs() << "offset (" << hexValue(Offset) << ") is beyond section size ("
+             << hexValue(Bytes.size()) << "); malformed input?\n";
+    });
+    return Error::success();
+  }
   uint8_t const *Begin = Bytes.data() + Offset;
   uint8_t const *End = Bytes.data() + Offset + Size;
 
diff --git a/llvm/unittests/DebugInfo/LogicalView/DWARFReaderTest.cpp b/llvm/unittests/DebugInfo/LogicalView/DWARFReaderTest.cpp
index c062c15481da9..7262da634d18d 100644
--- a/llvm/unittests/DebugInfo/LogicalView/DWARFReaderTest.cpp
+++ b/llvm/unittests/DebugInfo/LogicalView/DWARFReaderTest.cpp
@@ -30,6 +30,7 @@ extern const char *TestMainArgv0;
 namespace {
 
 const char *DwarfClang = "test-dwarf-clang.o";
+const char *DwarfClangUnspecParams = "test-dwarf-clang-unspec-params.elf";
 const char *DwarfGcc = "test-dwarf-gcc.o";
 
 // Helper function to get the first compile unit.
@@ -37,7 +38,7 @@ LVScopeCompileUnit *getFirstCompileUnit(LVScopeRoot *Root) {
   EXPECT_NE(Root, nullptr);
   const LVScopes *CompileUnits = Root->getScopes();
   EXPECT_NE(CompileUnits, nullptr);
-  EXPECT_EQ(CompileUnits->size(), 1u);
+  EXPECT_GT(CompileUnits->size(), 0u);
 
   LVScopes::const_iterator Iter = CompileUnits->begin();
   EXPECT_NE(Iter, nullptr);
@@ -124,6 +125,32 @@ void checkElementProperties(LVReader *Reader) {
   ASSERT_EQ(Lines->size(), 0x12u);
 }
 
+// Check proper handling of DW_AT_unspecified_parameters in
+// LVScope::addMissingElements().
+void checkUnspecifiedParameters(LVReader *Reader) {
+  LVScopeRoot *Root = Reader->getScopesRoot();
+  LVScopeCompileUnit *CompileUnit = getFirstCompileUnit(Root);
+
+  EXPECT_EQ(Root->getFileFormatName(), "elf64-x86-64");
+  EXPECT_EQ(Root->getName(), DwarfClangUnspecParams);
+
+  const LVPublicNames &PublicNames = CompileUnit->getPublicNames();
+  ASSERT_EQ(PublicNames.size(), 1u);
+
+  LVPublicNames::const_iterator IterNames = PublicNames.cbegin();
+  LVScope *Function = (*IterNames).first;
+  EXPECT_EQ(Function->getName(), "foo_printf");
+  const LVElements *Elements = Function->getChildren();
+  ASSERT_NE(Elements, nullptr);
+  EXPECT_EQ(std::any_of(
+                Elements->begin(), Elements->end(),
+                [](const LVElement *elt) {
+                  return elt->getIsSymbol() &&
+                         static_cast<const LVSymbol *>(elt)->getIsUnspecified();
+                }),
+            true);
+}
+
 // Check the logical elements selection.
 void checkElementSelection(LVReader *Reader) {
   LVScopeRoot *Root = Reader->getScopesRoot();
@@ -253,6 +280,7 @@ void elementProperties(SmallString<128> &InputsDir) {
   ReaderOptions.setAttributePublics();
   ReaderOptions.setAttributeRange();
   ReaderOptions.setAttributeLocation();
+  ReaderOptions.setAttributeInserted();
   ReaderOptions.setPrintAll();
   ReaderOptions.resolveDependencies();
 
@@ -264,6 +292,9 @@ void elementProperties(SmallString<128> &InputsDir) {
   std::unique_ptr<LVReader> Reader =
       createReader(ReaderHandler, InputsDir, DwarfClang);
   checkElementProperties(Reader.get());
+
+  Reader = createReader(ReaderHandler, InputsDir, DwarfClangUnspecParams);
+  checkUnspecifiedParameters(Reader.get());
 }
 
 // Logical elements selection.
diff --git a/llvm/unittests/DebugInfo/LogicalView/Inputs/test-dwarf-clang-unspec-params.elf b/llvm/unittests/DebugInfo/LogicalView/Inputs/test-dwarf-clang-unspec-params.elf
new file mode 100755
index 0000000000000..67c6e71fbf7b9
Binary files /dev/null and b/llvm/unittests/DebugInfo/LogicalView/Inputs/test-dwarf-clang-unspec-params.elf differ

@CarlosAlbertoEnciso
Copy link
Member

CarlosAlbertoEnciso commented Apr 30, 2025

Using the elf from your test case, I managed to reproduce the crash with a release build using the command line:
llmv-debuginfo-analyzer --attribute=inserted --print=symbols test-dwarf-clang-unspec-params.elf

Invalid symbol kind.
UNREACHABLE executed at X:\debuginfo-analyzer\llvm-project\llvm\lib\DebugInfo\LogicalView\Core\LVScope.cpp:341!
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
Stack dump:
...

if (Reference->getIsConstant())
Symbol->setIsConstant();
else if (Reference->getIsParameter())
Symbol->setIsParameter();
else if (Reference->getIsVariable())
Symbol->setIsVariable();
else if (Reference->getIsUnspecified())
Symbol->setIsUnspecified();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good finding.

<< hexValue(Bytes.size()) << "); malformed input?\n";
});
return Error::success(); // Continue; returning error aborts reader.
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should handle this condition as an error and abort the reader. May be something like:

if (Offset > Bytes.size()) {
  LLVM_DEBUG({.....});
  return createStringError(errc::bad_address, .......);
}

@dwblaikie dwblaikie removed their request for review May 1, 2025 20:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants