[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