llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
28.03k stars 11.58k forks source link

UNREACHABLE executed at llvm/tools/llvm-readobj/Win64EHDumper.cpp:97! #109251

Open glandium opened 11 hours ago

glandium commented 11 hours ago

Running llvm-readobj -a or llvm-readobj --unwind on a binary that was produced by lld, I'm getting the following:

Invalid unwind code
UNREACHABLE executed at /tmp/llvm/llvm/tools/llvm-readobj/Win64EHDumper.cpp:97!
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
Stack dump:
0.  Program arguments: /tmp/llvm/obj/bin/llvm-readobj --unwind /tmp/crashreporter-6497c44cdd21c683.exe
 #0 0x00007f9012cc1f1d llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) /tmp/llvm/llvm/lib/Support/Unix/Signals.inc:723:11
 #1 0x00007f9012cc240b PrintStackTraceSignalHandler(void*) /tmp/llvm/llvm/lib/Support/Unix/Signals.inc:798:1
 #2 0x00007f9012cc0476 llvm::sys::RunSignalHandlers() /tmp/llvm/llvm/lib/Support/Signals.cpp:105:5
 #3 0x00007f9012cc2bc5 SignalHandler(int) /tmp/llvm/llvm/lib/Support/Unix/Signals.inc:413:1
 #4 0x00007f9010f5c050 (/lib/x86_64-linux-gnu/libc.so.6+0x3c050)
 #5 0x00007f9010faae2c __pthread_kill_implementation ./nptl/pthread_kill.c:44:76
 #6 0x00007f9010f5bfb2 raise ./signal/../sysdeps/posix/raise.c:27:6
 #7 0x00007f9010f46472 abort ./stdlib/abort.c:81:7
 #8 0x00007f9012b8e730 llvm::install_out_of_memory_new_handler() /tmp/llvm/llvm/lib/Support/ErrorHandling.cpp:195:0
 #9 0x000056062c9a5e70 getNumUsedSlots(llvm::Win64EH::UnwindCode const&) /tmp/llvm/llvm/tools/llvm-readobj/Win64EHDumper.cpp:102:5
#10 0x000056062c9a663d llvm::Win64EH::Dumper::printUnwindInfo(llvm::Win64EH::Dumper::Context const&, llvm::object::coff_section const*, long, llvm::Win64EH::UnwindInfo const&) /tmp/llvm/llvm/tools/llvm-readobj/Win64EHDumper.cpp:334:16
#11 0x000056062c9a6bcd llvm::Win64EH::Dumper::printRuntimeFunction(llvm::Win64EH::Dumper::Context const&, llvm::object::coff_section const*, unsigned long, llvm::Win64EH::RuntimeFunction const&) /tmp/llvm/llvm/tools/llvm-readobj/Win64EHDumper.cpp:390:1
#12 0x000056062c9a71bf llvm::Win64EH::Dumper::printData(llvm::Win64EH::Dumper::Context const&) /tmp/llvm/llvm/tools/llvm-readobj/Win64EHDumper.cpp:420:7
#13 0x000056062c7b2284 (anonymous namespace)::COFFDumper::printUnwindInfo() /tmp/llvm/llvm/tools/llvm-readobj/COFFDumper.cpp:1797:5
#14 0x000056062c980939 dumpObject(llvm::object::ObjectFile&, llvm::ScopedPrinter&, llvm::object::Archive const*) /tmp/llvm/llvm/tools/llvm-readobj/llvm-readobj.cpp:444:21
#15 0x000056062c9800d4 dumpCOFFObject(llvm::object::COFFObjectFile*, llvm::ScopedPrinter&) /tmp/llvm/llvm/tools/llvm-readobj/llvm-readobj.cpp:588:46
#16 0x000056062c97f0b5 dumpInput(llvm::StringRef, llvm::ScopedPrinter&) /tmp/llvm/llvm/tools/llvm-readobj/llvm-readobj.cpp:637:5
#17 0x000056062c97dec3 llvm_readobj_main(int, char**, llvm::ToolContext const&) /tmp/llvm/llvm/tools/llvm-readobj/llvm-readobj.cpp:720:29
#18 0x000056062c9b71e5 main /tmp/llvm/obj/tools/llvm-readobj/llvm-readobj-driver.cpp:17:3
#19 0x00007f9010f4724a __libc_start_call_main ./csu/../sysdeps/nptl/libc_start_call_main.h:74:3
#20 0x00007f9010f47305 call_init ./csu/../csu/libc-start.c:128:20
#21 0x00007f9010f47305 __libc_start_main ./csu/../csu/libc-start.c:347:5
#22 0x000056062c799ae1 _start (/tmp/llvm/obj/bin/llvm-readobj+0x46ae1)

The (probably broken) file can be found at https://drive.google.com/file/d/1mMhbAX9SikIwSRXNRQA22frfazJACy6k/view?usp=sharing

Cc @mstorsjo

mstorsjo commented 10 hours ago

Yeah, unfortunately quite a bit of code in the readobj tools use asserts/unreachable to validate input data, which isn't quite right in general.

A crude patch like this avoids this particular issue, but there's probably other cases lurking as well:

diff --git a/llvm/tools/llvm-readobj/Win64EHDumper.cpp b/llvm/tools/llvm-readobj/Win64EHDumper.cpp
index e4bd77219151..27eefe45e263 100644
--- a/llvm/tools/llvm-readobj/Win64EHDumper.cpp
+++ b/llvm/tools/llvm-readobj/Win64EHDumper.cpp
@@ -92,9 +92,10 @@ static StringRef getUnwindRegisterName(uint8_t Reg) {
 }

 // Calculates the number of array slots required for the unwind code.
-static unsigned getNumUsedSlots(const UnwindCode &UnwindCode) {
+static Expected<unsigned> getNumUsedSlots(const UnwindCode &UnwindCode) {
   switch (UnwindCode.getUnwindOp()) {
-  default: llvm_unreachable("Invalid unwind code");
+  default:
+    return createError("Invalid unwind code");
   case UOP_PushNonVol:
   case UOP_AllocSmall:
   case UOP_SetFPReg:
@@ -254,8 +255,12 @@ void Dumper::printRuntimeFunctionEntry(const Context &Ctx,
 // Prints one unwind code. Because an unwind code can occupy up to 3 slots in
 // the unwind codes array, this function requires that the correct number of
 // slots is provided.
-void Dumper::printUnwindCode(const UnwindInfo& UI, ArrayRef<UnwindCode> UC) {
-  assert(UC.size() >= getNumUsedSlots(UC[0]));
+Error Dumper::printUnwindCode(const UnwindInfo& UI, ArrayRef<UnwindCode> UC) {
+  Expected<unsigned> slotsOrErr = getNumUsedSlots(UC[0]);
+  if (!slotsOrErr)
+    return slotsOrErr.takeError();
+  if (UC.size() < *slotsOrErr)
+    return createError("truncated unwind code");

   SW.startLine() << format("0x%02X: ", unsigned(UC[0].u.CodeOffset))
                  << getUnwindCodeTypeName(UC[0].getUnwindOp());
@@ -309,6 +314,7 @@ void Dumper::printUnwindCode(const UnwindInfo& UI, ArrayRef<UnwindCode> UC) {
   }

   OS << "\n";
+  return Error::success();
 }

 void Dumper::printUnwindInfo(const Context &Ctx, const coff_section *Section,
@@ -331,14 +337,20 @@ void Dumper::printUnwindInfo(const Context &Ctx, const coff_section *Section,
     ListScope UCS(SW, "UnwindCodes");
     ArrayRef<UnwindCode> UC(&UI.UnwindCodes[0], UI.NumCodes);
     for (const UnwindCode *UCI = UC.begin(), *UCE = UC.end(); UCI < UCE; ++UCI) {
-      unsigned UsedSlots = getNumUsedSlots(*UCI);
-      if (UsedSlots > UC.size()) {
-        errs() << "corrupt unwind data";
+      Expected<unsigned> UsedSlots = getNumUsedSlots(*UCI);
+      if (!UsedSlots) {
+        errs() << "corrupt unwind data: " << toString(UsedSlots.takeError()) << "\n";
+        return;
+      } else if (*UsedSlots > UC.size()) {
+        errs() << "corrupt unwind data\n";
         return;
       }

-      printUnwindCode(UI, ArrayRef(UCI, UCE));
-      UCI = UCI + UsedSlots - 1;
+      if (Error E = printUnwindCode(UI, ArrayRef(UCI, UCE))) {
+        errs() << "corrupt unwind data: " << toString(std::move(E)) << "\n";
+        return;
+      }
+      UCI = UCI + *UsedSlots - 1;
     }
   }

diff --git a/llvm/tools/llvm-readobj/Win64EHDumper.h b/llvm/tools/llvm-readobj/Win64EHDumper.h
index 97458c916bec..2ef5fd74604a 100644
--- a/llvm/tools/llvm-readobj/Win64EHDumper.h
+++ b/llvm/tools/llvm-readobj/Win64EHDumper.h
@@ -44,7 +44,7 @@ private:
                                  const object::coff_section *Section,
                                  uint64_t SectionOffset,
                                  const RuntimeFunction &RF);
-  void printUnwindCode(const UnwindInfo& UI, ArrayRef<UnwindCode> UC);
+  Error printUnwindCode(const UnwindInfo& UI, ArrayRef<UnwindCode> UC);
   void printUnwindInfo(const Context &Ctx, const object::coff_section *Section,
                        off_t Offset, const UnwindInfo &UI);
   void printRuntimeFunction(const Context &Ctx,