[llvm] 56970ec - [llvm-readobj] - --gnu-hash-table: do not crash when the GNU hash table goes past the EOF.

Georgii Rymar via llvm-commits llvm-commits at lists.llvm.org
Fri May 15 01:41:01 PDT 2020


Author: Georgii Rymar
Date: 2020-05-15T11:33:23+03:00
New Revision: 56970ec6a0bed5ba4312872eed13137ca43f67fb

URL: https://github.com/llvm/llvm-project/commit/56970ec6a0bed5ba4312872eed13137ca43f67fb
DIFF: https://github.com/llvm/llvm-project/commit/56970ec6a0bed5ba4312872eed13137ca43f67fb.diff

LOG: [llvm-readobj] - --gnu-hash-table: do not crash when the GNU hash table goes past the EOF.

We might have a scenario where a the `GbuHashTable` variable correctly points
to a place inside the file (we validate this fact early in `parseDynamicTable`),
but nbuckets/maskwords fields are broken in the way the code tries
to read the data past the EOF. This patch fixes the issue.

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

Added: 
    

Modified: 
    llvm/test/tools/llvm-readobj/ELF/gnuhash.test
    llvm/tools/llvm-readobj/ELFDumper.cpp
    llvm/tools/llvm-readobj/ObjDumper.h
    llvm/tools/llvm-readobj/llvm-readobj.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/test/tools/llvm-readobj/ELF/gnuhash.test b/llvm/test/tools/llvm-readobj/ELF/gnuhash.test
index c680e0071f28..71e6551a5771 100644
--- a/llvm/test/tools/llvm-readobj/ELF/gnuhash.test
+++ b/llvm/test/tools/llvm-readobj/ELF/gnuhash.test
@@ -1,6 +1,6 @@
 ## Check how the GNU Hash section is dumped with --gnu-hash-table.
 
-# RUN: yaml2obj --docnum=1 %s -o %t.x64
+# RUN: yaml2obj --docnum=1 -D MASKWORDS=2 -D NBUCKETS=3 %s -o %t.x64
 # RUN: yaml2obj --docnum=2 %s -o %t.x32
 
 # RUN: llvm-readobj --gnu-hash-table %t.x64 | FileCheck %s
@@ -32,6 +32,10 @@ Sections:
     Header:
       SymNdx: 0x1
       Shift2: 0x2
+## The number of words in the Bloom filter. The value of 2 is no-op.
+      MaskWords: [[MASKWORDS]]
+## The number of hash buckets. The value of 3 is no-op.
+      NBuckets:  [[NBUCKETS]]
     BloomFilter: [0x3, 0x4]
     HashBuckets: [0x5, 0x6, 0x7]
     HashValues:  [0x8, 0x9, 0xA, 0xB]
@@ -314,3 +318,27 @@ ProgramHeaders:
     Sections:
       - Section: .gnu.hash
       - Section: .dynamic
+
+## Check we report a proper warning when a hash table goes past the end of the file.
+
+## Case A: the 'nbuckets' field is set so that the table goes past the end of the file.
+# RUN: yaml2obj --docnum=1 -D MASKWORDS=4294967295 -D NBUCKETS=3 %s -o %t.err.maskwords
+# RUN: llvm-readobj --gnu-hash-table %t.err.maskwords 2>&1 | \
+# RUN:   FileCheck %s -DFILE=%t.err.maskwords -DMASKWORDS=4294967295 -DNBUCKETS=3 --check-prefix=ERR
+# RUN: llvm-readelf --gnu-hash-table %t.err.maskwords 2>&1 | \
+# RUN:   FileCheck %s -DFILE=%t.err.maskwords -DMASKWORDS=4294967295 -DNBUCKETS=3 --check-prefix=ERR
+
+## Case B: the 'maskwords' field is set so that the table goes past the end of the file.
+# RUN: yaml2obj --docnum=1 -D MASKWORDS=2 -D NBUCKETS=4294967295 %s -o %t.err.nbuckets
+# RUN: llvm-readobj --gnu-hash-table %t.err.nbuckets 2>&1 | \
+# RUN:   FileCheck %s -DFILE=%t.err.nbuckets -DMASKWORDS=2 -DNBUCKETS=4294967295 --check-prefix=ERR
+# RUN: llvm-readelf --gnu-hash-table %t.err.nbuckets 2>&1 | \
+# RUN:   FileCheck %s -DFILE=%t.err.nbuckets -DMASKWORDS=2 -DNBUCKETS=4294967295 --check-prefix=ERR
+
+# ERR:      GnuHashTable {
+# ERR-NEXT:   Num Buckets: [[NBUCKETS]]
+# ERR-NEXT:   First Hashed Symbol Index: 1
+# ERR-NEXT:   Num Mask Words: [[MASKWORDS]]
+# ERR-NEXT:   Shift Count: 2
+# ERR-NEXT: warning: '[[FILE]]': unable to dump the SHT_GNU_HASH section at 0x78: it goes past the end of the file
+# ERR-NEXT: }

diff  --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index b5d9a274e31e..21ba0f09bb38 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -221,7 +221,7 @@ template <typename ELFT> class ELFDumper : public ObjDumper {
   void printProgramHeaders(bool PrintProgramHeaders,
                            cl::boolOrDefault PrintSectionMapping) override;
   void printHashTable() override;
-  void printGnuHashTable() override;
+  void printGnuHashTable(const object::ObjectFile *Obj) override;
   void printLoadName() override;
   void printVersionInfo() override;
   void printGroupSections() override;
@@ -2666,7 +2666,8 @@ template <typename ELFT> void ELFDumper<ELFT>::printHashTable() {
   W.printList("Chains", HashTable->chains());
 }
 
-template <typename ELFT> void ELFDumper<ELFT>::printGnuHashTable() {
+template <typename ELFT>
+void ELFDumper<ELFT>::printGnuHashTable(const object::ObjectFile *Obj) {
   DictScope D(W, "GnuHashTable");
   if (!GnuHashTable)
     return;
@@ -2675,6 +2676,25 @@ template <typename ELFT> void ELFDumper<ELFT>::printGnuHashTable() {
   W.printNumber("Num Mask Words", GnuHashTable->maskwords);
   W.printNumber("Shift Count", GnuHashTable->shift2);
 
+  MemoryBufferRef File = Obj->getMemoryBufferRef();
+  const char *TableData = reinterpret_cast<const char *>(GnuHashTable);
+  assert(TableData >= File.getBufferStart() &&
+         TableData < File.getBufferEnd() &&
+         "GnuHashTable must always point to a location inside the file");
+
+  uint64_t TableOffset = TableData - File.getBufferStart();
+  if (TableOffset +
+          /*Header size:*/ 16 + GnuHashTable->nbuckets * 4 +
+          GnuHashTable->maskwords * sizeof(typename ELFT::Off) >=
+      File.getBufferSize()) {
+    reportWarning(createError("unable to dump the SHT_GNU_HASH "
+                              "section at 0x" +
+                              Twine::utohexstr(TableOffset) +
+                              ": it goes past the end of the file"),
+                  ObjF->getFileName());
+    return;
+  }
+
   ArrayRef<typename ELFT::Off> BloomFilter = GnuHashTable->filter();
   W.printHexList("Bloom Filter", BloomFilter);
 

diff  --git a/llvm/tools/llvm-readobj/ObjDumper.h b/llvm/tools/llvm-readobj/ObjDumper.h
index 3fc8d3e79ac1..a0f69edc9071 100644
--- a/llvm/tools/llvm-readobj/ObjDumper.h
+++ b/llvm/tools/llvm-readobj/ObjDumper.h
@@ -59,7 +59,7 @@ class ObjDumper {
   virtual void printNeededLibraries() { }
   virtual void printSectionAsHex(StringRef SectionName) {}
   virtual void printHashTable() { }
-  virtual void printGnuHashTable() { }
+  virtual void printGnuHashTable(const object::ObjectFile *Obj) {}
   virtual void printHashSymbols() {}
   virtual void printLoadName() {}
   virtual void printVersionInfo() {}

diff  --git a/llvm/tools/llvm-readobj/llvm-readobj.cpp b/llvm/tools/llvm-readobj/llvm-readobj.cpp
index e87031ff8b95..85409389eccd 100644
--- a/llvm/tools/llvm-readobj/llvm-readobj.cpp
+++ b/llvm/tools/llvm-readobj/llvm-readobj.cpp
@@ -489,7 +489,7 @@ static void dumpObject(const ObjectFile *Obj, ScopedPrinter &Writer,
   if (opts::HashTable)
     Dumper->printHashTable();
   if (opts::GnuHashTable)
-    Dumper->printGnuHashTable();
+    Dumper->printGnuHashTable(Obj);
   if (opts::VersionInfo)
     Dumper->printVersionInfo();
   if (Obj->isELF()) {


        


More information about the llvm-commits mailing list