[llvm] c68ee6d - [llvm-readelf] - --elf-hash-histogram: do not crash when the .gnu.hash goes past the EOF.
Georgii Rymar via llvm-commits
llvm-commits at lists.llvm.org
Fri May 29 03:30:52 PDT 2020
Author: Georgii Rymar
Date: 2020-05-29T13:29:48+03:00
New Revision: c68ee6da283c5697e935d1f9b7401c086cb18e03
URL: https://github.com/llvm/llvm-project/commit/c68ee6da283c5697e935d1f9b7401c086cb18e03
DIFF: https://github.com/llvm/llvm-project/commit/c68ee6da283c5697e935d1f9b7401c086cb18e03.diff
LOG: [llvm-readelf] - --elf-hash-histogram: do not crash when the .gnu.hash goes past the EOF.
llvm-readelf might crash when the .gnu.hash table goes past the EOF.
This patch splits and updates the code of a helper function `checkGNUHashTable`,
which is similar to `checkHashTable` and fixes the issue.
Differential revision: https://reviews.llvm.org/D80215
Added:
Modified:
llvm/test/tools/llvm-readobj/ELF/gnuhash.test
llvm/test/tools/llvm-readobj/ELF/hash-histogram.test
llvm/tools/llvm-readobj/ELFDumper.cpp
Removed:
################################################################################
diff --git a/llvm/test/tools/llvm-readobj/ELF/gnuhash.test b/llvm/test/tools/llvm-readobj/ELF/gnuhash.test
index 1ba746cd9d98..415c3b4f4b87 100644
--- a/llvm/test/tools/llvm-readobj/ELF/gnuhash.test
+++ b/llvm/test/tools/llvm-readobj/ELF/gnuhash.test
@@ -301,3 +301,8 @@ ProgramHeaders:
# 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: }
+
+## Check we report a single warning about the broken GNU hash table when both
+## --gnu-hash-table and --elf-hash-histogram options are requested.
+# RUN: llvm-readelf --gnu-hash-table --elf-hash-histogram %t.err.nbuckets 2>&1 | \
+# RUN: FileCheck %s -DFILE=%t.err.nbuckets -DMASKWORDS=2 -DNBUCKETS=4294967295 --check-prefix=ERR --implicit-check-not=warning
diff --git a/llvm/test/tools/llvm-readobj/ELF/hash-histogram.test b/llvm/test/tools/llvm-readobj/ELF/hash-histogram.test
index 5447bffceec3..4ec772a18f82 100644
--- a/llvm/test/tools/llvm-readobj/ELF/hash-histogram.test
+++ b/llvm/test/tools/llvm-readobj/ELF/hash-histogram.test
@@ -266,3 +266,55 @@ ProgramHeaders:
- Section: .hash
- Section: .gnu.hash
- Section: .dynamic
+
+## Check we report a proper warning when the GNU hash table goes past the end of the file.
+
+## Case A: the 'nbuckets' field is set so that the GNU hash table goes past the end of the file.
+## The value of 1 for the NBUCKETS is no-op.
+# RUN: yaml2obj --docnum=6 -D MASKWORDS=4294967295 -D NBUCKETS=1 %s -o %t7
+# RUN: llvm-readelf --elf-hash-histogram %t7 2>&1 | \
+# RUN: FileCheck %s -DFILE=%t7 --check-prefix=ERR5 --implicit-check-not="Histogram"
+
+# ERR5: warning: '[[FILE]]': unable to dump the SHT_GNU_HASH section at 0x78: it goes past the end of the file
+
+## Case B: the 'maskwords' field is set so that the GNU hash table goes past the end of the file.
+## The value of 1 for the MASKWORDS is no-op.
+# RUN: yaml2obj --docnum=6 -D MASKWORDS=1 -D NBUCKETS=4294967295 %s -o %t8
+# RUN: llvm-readelf --elf-hash-histogram %t8 2>&1 | \
+# RUN: FileCheck %s -DFILE=%t8 --check-prefix=ERR5 --implicit-check-not="Histogram"
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_DYN
+ Machine: EM_X86_64
+Sections:
+ - Name: .gnu.hash
+ Type: SHT_GNU_HASH
+ Flags: [ SHF_ALLOC ]
+ Header:
+ SymNdx: 0x0
+ Shift2: 0x0
+## The number of words in the Bloom filter.
+ MaskWords: [[MASKWORDS]]
+## The number of hash buckets.
+ NBuckets: [[NBUCKETS]]
+ BloomFilter: [ 0x0 ]
+ HashBuckets: [ 0x0 ]
+ HashValues: [ 0x0 ]
+ - Name: .dynamic
+ Type: SHT_DYNAMIC
+ Flags: [ SHF_ALLOC ]
+ Link: .dynstr
+ Entries:
+ - Tag: DT_GNU_HASH
+ Value: 0x0
+ - Tag: DT_NULL
+ Value: 0x0
+DynamicSymbols: []
+ProgramHeaders:
+ - Type: PT_LOAD
+ Sections:
+ - Section: .gnu.hash
+ - Section: .dynamic
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 84a68b17b298..eebe65f8400f 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -2666,6 +2666,28 @@ static bool checkHashTable(const ELFFile<ELFT> *Obj,
return true;
}
+template <class ELFT>
+static Error checkGNUHashTable(const ELFFile<ELFT> *Obj,
+ const typename ELFT::GnuHash *GnuHashTable,
+ bool *IsHeaderValid = nullptr) {
+ const uint8_t *TableData = reinterpret_cast<const uint8_t *>(GnuHashTable);
+ assert(TableData >= Obj->base() &&
+ TableData < Obj->base() + Obj->getBufSize() &&
+ "GnuHashTable must always point to a location inside the file");
+
+ uint64_t TableOffset = TableData - Obj->base();
+ if (IsHeaderValid)
+ *IsHeaderValid = TableOffset + /*Header size:*/ 16 < Obj->getBufSize();
+ if (TableOffset + 16 + GnuHashTable->nbuckets * 4 +
+ GnuHashTable->maskwords * sizeof(typename ELFT::Off) >=
+ Obj->getBufSize())
+ return createError("unable to dump the SHT_GNU_HASH "
+ "section at 0x" +
+ Twine::utohexstr(TableOffset) +
+ ": it goes past the end of the file");
+ return Error::success();
+}
+
template <typename ELFT> void ELFDumper<ELFT>::printHashTable() {
DictScope D(W, "HashTable");
if (!HashTable ||
@@ -2682,27 +2704,19 @@ void ELFDumper<ELFT>::printGnuHashTable(const object::ObjectFile *Obj) {
DictScope D(W, "GnuHashTable");
if (!GnuHashTable)
return;
- W.printNumber("Num Buckets", GnuHashTable->nbuckets);
- W.printNumber("First Hashed Symbol Index", GnuHashTable->symndx);
- 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());
+ bool IsHeaderValid;
+ Error Err =
+ checkGNUHashTable<ELFT>(ObjF->getELFFile(), GnuHashTable, &IsHeaderValid);
+ if (IsHeaderValid) {
+ W.printNumber("Num Buckets", GnuHashTable->nbuckets);
+ W.printNumber("First Hashed Symbol Index", GnuHashTable->symndx);
+ W.printNumber("Num Mask Words", GnuHashTable->maskwords);
+ W.printNumber("Shift Count", GnuHashTable->shift2);
+ }
+
+ if (Err) {
+ reportUniqueWarning(std::move(Err));
return;
}
@@ -4680,7 +4694,10 @@ void GNUStyle<ELFT>::printHashHistograms(const ELFFile<ELFT> *Obj) {
// Print histogram for the .gnu.hash section.
if (const Elf_GnuHash *GnuHashTable = this->dumper()->getGnuHashTable())
- printGnuHashHistogram(*GnuHashTable);
+ if (Error E = checkGNUHashTable<ELFT>(Obj, GnuHashTable))
+ this->reportUniqueWarning(std::move(E));
+ else
+ printGnuHashHistogram(*GnuHashTable);
}
template <class ELFT>
More information about the llvm-commits
mailing list