[llvm] f527e6f - [llvm-readobj] - Do not crash when SHT_HASH table is broken.
Georgii Rymar via llvm-commits
llvm-commits at lists.llvm.org
Wed Apr 1 08:03:29 PDT 2020
Author: Georgii Rymar
Date: 2020-04-01T18:03:02+03:00
New Revision: f527e6f2e11dcda8e71c21138fa15e3a3b9e9917
URL: https://github.com/llvm/llvm-project/commit/f527e6f2e11dcda8e71c21138fa15e3a3b9e9917
DIFF: https://github.com/llvm/llvm-project/commit/f527e6f2e11dcda8e71c21138fa15e3a3b9e9917.diff
LOG: [llvm-readobj] - Do not crash when SHT_HASH table is broken.
We have scenarios when the logic of --elf-hash-histogram/--hash-symbols/--hash-table
options might crash when given a broken hash table.
This patch adds pre-checks for tables for these 3 options
and provides test cases.
Differential revision: https://reviews.llvm.org/D77147
Added:
Modified:
llvm/test/tools/llvm-readobj/ELF/hash-histogram.test
llvm/test/tools/llvm-readobj/ELF/hash-symbols.test
llvm/test/tools/llvm-readobj/ELF/hash-table.test
llvm/tools/llvm-readobj/ELFDumper.cpp
Removed:
################################################################################
diff --git a/llvm/test/tools/llvm-readobj/ELF/hash-histogram.test b/llvm/test/tools/llvm-readobj/ELF/hash-histogram.test
index cdbec32efa24..f7551b481a86 100644
--- a/llvm/test/tools/llvm-readobj/ELF/hash-histogram.test
+++ b/llvm/test/tools/llvm-readobj/ELF/hash-histogram.test
@@ -112,3 +112,98 @@ ProgramHeaders:
Sections:
- Section: .hash
- Section: .dynamic
+
+## Each SHT_HASH section starts with two 32-bit fields: nbucket and nchain.
+## Check we report an error when a DT_HASH value points to data that has size less than 8 bytes.
+
+# RUN: yaml2obj --docnum=3 %s -o %t3.o
+# RUN: llvm-readelf --elf-hash-histogram %t3.o 2>&1 | FileCheck %s --check-prefix=ERR1 -DFILE=%t3.o
+
+# ERR1: warning: '[[FILE]]': the hash table at offset 0x2b1 goes past the end of the file (0x2b8){{$}}
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_DYN
+ Machine: EM_X86_64
+Sections:
+ - Name: .hash
+ Type: SHT_HASH
+ Flags: [ SHF_ALLOC ]
+ Bucket: [ 0 ]
+ Chain: [ 0 ]
+ - Name: .dynamic
+ Type: SHT_DYNAMIC
+ Flags: [ SHF_WRITE, SHF_ALLOC ]
+ Entries:
+ - Tag: DT_HASH
+ Value: 0x239
+ - Tag: DT_NULL
+ Value: 0x0
+DynamicSymbols: []
+ProgramHeaders:
+ - Type: PT_LOAD
+ FileSize: 0x23a
+ Sections:
+ - Section: .hash
+ - Section: .dynamic
+
+## Check we report a warning when the hash table goes past the end of the file.
+
+## Case A.1: the hash table ends right before the EOF. We have a broken nbucket
+## field that has a value larger than the number of buckets.
+# RUN: yaml2obj --docnum=4 %s -o %t4.1.o -DNBUCKET=0x5d -DNCHAIN=0x1
+# RUN: llvm-readelf --elf-hash-histogram %t4.1.o 2>&1 | \
+# RUN: FileCheck %s --implicit-check-not={{.}} --allow-empty
+
+## Case A.2: the hash table ends 1 byte past the EOF. We have a broken nbucket
+## field that has a value larger than the number of buckets.
+# RUN: yaml2obj --docnum=4 %s -o %t4.2.o -DNBUCKET=0x5e -DNCHAIN=0x1
+# RUN: llvm-readelf --elf-hash-histogram %t4.2.o 2>&1 | \
+# RUN: FileCheck %s --check-prefix=ERR2 -DFILE=%t4.2.o --implicit-check-not="warning:"
+# ERR2: warning: '[[FILE]]': the hash table at offset 0x54 goes past the end of the file (0x1d4), nbucket = 94, nchain = 1{{$}}
+
+## Case B.1: the hash table ends right before the EOF. We have a broken nchain
+## field that has a value larger than the number of chains.
+# RUN: yaml2obj --docnum=4 %s -o %t4.3.o -DNBUCKET=0x1 -DNCHAIN=0x5d
+# RUN: llvm-readelf --elf-hash-histogram %t4.3.o 2>&1 | \
+# RUN: FileCheck %s --check-prefix=ERR3 -DFILE=%t4.3.o --implicit-check-not="warning:"
+# ERR3: warning: '[[FILE]]': hash table nchain (93)
diff ers from symbol count derived from SHT_DYNSYM section header (1){{$}}
+
+## Case B.2: the hash table ends 1 byte past the EOF. We have a broken nchain
+## field that has a value larger than the number of chains.
+# RUN: yaml2obj --docnum=4 %s -o %t4.4.o -DNBUCKET=0x1 -DNCHAIN=0x5e
+# RUN: llvm-readelf --elf-hash-histogram %t4.4.o 2>&1 | \
+# RUN: FileCheck %s --check-prefix=ERR4 -DFILE=%t4.4.o --implicit-check-not="warning:"
+# ERR4: warning: '[[FILE]]': hash table nchain (94)
diff ers from symbol count derived from SHT_DYNSYM section header (1){{$}}
+# ERR4: warning: '[[FILE]]': the hash table at offset 0x54 goes past the end of the file (0x1d4), nbucket = 1, nchain = 94{{$}}
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS32
+ Data: ELFDATA2LSB
+ Type: ET_DYN
+ Machine: EM_X86_64
+Sections:
+ - Name: .hash
+ Type: SHT_HASH
+ Flags: [ SHF_ALLOC ]
+ Bucket: [ 0 ]
+ NBucket: [[NBUCKET]]
+ Chain: [ 0 ]
+ NChain: [[NCHAIN]]
+ - Name: .dynamic
+ Type: SHT_DYNAMIC
+ Flags: [ SHF_WRITE, SHF_ALLOC ]
+ Entries:
+ - Tag: DT_HASH
+ Value: 0x0
+ - Tag: DT_NULL
+ Value: 0x0
+DynamicSymbols: []
+ProgramHeaders:
+ - Type: PT_LOAD
+ Sections:
+ - Section: .hash
+ - Section: .dynamic
diff --git a/llvm/test/tools/llvm-readobj/ELF/hash-symbols.test b/llvm/test/tools/llvm-readobj/ELF/hash-symbols.test
index 9434da1882a8..40e680e3b751 100644
--- a/llvm/test/tools/llvm-readobj/ELF/hash-symbols.test
+++ b/llvm/test/tools/llvm-readobj/ELF/hash-symbols.test
@@ -347,3 +347,104 @@ ProgramHeaders:
Sections:
- Section: .hash
- Section: .dynamic
+
+## Each SHT_HASH section starts with two 32-bit fields: nbucket and nchain.
+## Check we report an error when a DT_HASH value points to data that has size less than 8 bytes.
+
+# RUN: yaml2obj --docnum=6 %s -o %t6.o
+# RUN: llvm-readelf --hash-symbols %t6.o 2>&1 | FileCheck %s --check-prefix=ERR1 -DFILE=%t6.o
+
+# ERR1: warning: '[[FILE]]': the hash table at offset 0x2b1 goes past the end of the file (0x2b8){{$}}
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_DYN
+ Machine: EM_X86_64
+Sections:
+ - Name: .hash
+ Type: SHT_HASH
+ Flags: [ SHF_ALLOC ]
+ Bucket: [ 0 ]
+ Chain: [ 0 ]
+ - Name: .dynamic
+ Type: SHT_DYNAMIC
+ Flags: [ SHF_WRITE, SHF_ALLOC ]
+ Entries:
+ - Tag: DT_HASH
+ Value: 0x239
+ - Tag: DT_NULL
+ Value: 0x0
+DynamicSymbols: []
+ProgramHeaders:
+ - Type: PT_LOAD
+ FileSize: 0x23a
+ Sections:
+ - Section: .hash
+ - Section: .dynamic
+
+## Check we report a warning when the hash table goes past the end of the file.
+
+## Case A.1: the hash table ends right before the EOF. We have a broken nbucket
+## field that has a value larger than the number of buckets.
+# RUN: yaml2obj --docnum=7 %s -o %t7.1.o -DNBUCKET=0x5d -DNCHAIN=0x1
+# RUN: llvm-readelf --hash-symbols %t7.1.o 2>&1 | FileCheck %s --check-prefix=NOERR1
+# NOERR1: Symbol table of .hash for image:
+# NOERR1-NEXT: Num Buc: Value Size Type Bind Vis Ndx Name
+# NOERR1-NEXT-EMPTY:
+
+## Case A.2: the hash table ends 1 byte past the EOF. We have a broken nbucket
+## field that has a value larger than the number of buckets.
+# RUN: yaml2obj --docnum=7 %s -o %t7.2.o -DNBUCKET=0x5e -DNCHAIN=0x1
+# RUN: llvm-readelf --hash-symbols %t7.2.o 2>&1 | FileCheck %s --check-prefix=ERR2 -DFILE=%t7.2.o
+# ERR2: Symbol table of .hash for image:
+# ERR2-NEXT: warning: '[[FILE]]': the hash table at offset 0x54 goes past the end of the file (0x1d4), nbucket = 94, nchain = 1{{$}}
+# ERR2-NOT: {{.}}
+
+## Case B.1: the hash table ends right before the EOF. We have a broken nchain
+## field that has a value larger than the number of chains.
+# RUN: yaml2obj --docnum=7 %s -o %t7.3.o -DNBUCKET=0x1 -DNCHAIN=0x5d
+# RUN: llvm-readelf --hash-symbols %t7.3.o 2>&1 | \
+# RUN: FileCheck %s --implicit-check-not="warning:" --check-prefix=NOERR2 -DFILE=%t7.3.o
+# NOERR2: warning: '[[FILE]]': hash table nchain (93)
diff ers from symbol count derived from SHT_DYNSYM section header (1)
+# NOERR2: Symbol table of .hash for image:
+# NOERR2-NEXT: Num Buc: Value Size Type Bind Vis Ndx Name
+# NOERR2-NOT: {{.}}
+
+## Case B.2: the hash table ends 1 byte past the EOF. We have a broken nchain
+## field that has a value larger than the number of chains.
+# RUN: yaml2obj --docnum=7 %s -o %t7.4.o -DNBUCKET=0x1 -DNCHAIN=0x5e
+# RUN: llvm-readelf --hash-symbols %t7.4.o 2>&1 | FileCheck %s --check-prefix=ERR3 -DFILE=%t7.4.o
+# ERR3: Symbol table of .hash for image:
+# ERR3-NEXT: warning: '[[FILE]]': the hash table at offset 0x54 goes past the end of the file (0x1d4), nbucket = 1, nchain = 94{{$}}
+# ERR3-NOT: {{.}}
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS32
+ Data: ELFDATA2LSB
+ Type: ET_DYN
+ Machine: EM_X86_64
+Sections:
+ - Name: .hash
+ Type: SHT_HASH
+ Flags: [ SHF_ALLOC ]
+ Bucket: [ 0 ]
+ NBucket: [[NBUCKET]]
+ Chain: [ 0 ]
+ NChain: [[NCHAIN]]
+ - Name: .dynamic
+ Type: SHT_DYNAMIC
+ Flags: [ SHF_WRITE, SHF_ALLOC ]
+ Entries:
+ - Tag: DT_HASH
+ Value: 0x0
+ - Tag: DT_NULL
+ Value: 0x0
+DynamicSymbols: []
+ProgramHeaders:
+ - Type: PT_LOAD
+ Sections:
+ - Section: .hash
+ - Section: .dynamic
diff --git a/llvm/test/tools/llvm-readobj/ELF/hash-table.test b/llvm/test/tools/llvm-readobj/ELF/hash-table.test
index 2abfdd01baf2..8cbe615eee22 100644
--- a/llvm/test/tools/llvm-readobj/ELF/hash-table.test
+++ b/llvm/test/tools/llvm-readobj/ELF/hash-table.test
@@ -115,3 +115,133 @@ ProgramHeaders:
VAddr: 0x1010
Sections:
- Section: .dynamic
+
+## Each SHT_HASH section starts with two 32-bit fields: nbucket and nchain.
+## Check we report an error when a DT_HASH value points to data that has size less than 8 bytes.
+
+# RUN: yaml2obj --docnum=4 %s -o %t4.o
+# RUN: llvm-readelf --hash-table %t4.o 2>&1 | FileCheck %s --check-prefix=ERR1 -DFILE=%t4.o
+# RUN: llvm-readobj --hash-table %t4.o 2>&1 | FileCheck %s --check-prefix=ERR1 -DFILE=%t4.o
+
+# ERR1: HashTable {
+# ERR1-NEXT: warning: '[[FILE]]': the hash table at offset 0x2b1 goes past the end of the file (0x2b8){{$}}
+# ERR1-NEXT: }
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_DYN
+ Machine: EM_X86_64
+Sections:
+ - Name: .hash
+ Type: SHT_HASH
+ Flags: [ SHF_ALLOC ]
+ Bucket: [ 0 ]
+ Chain: [ 0 ]
+ - Name: .dynamic
+ Type: SHT_DYNAMIC
+ Flags: [ SHF_WRITE, SHF_ALLOC ]
+ Entries:
+ - Tag: DT_HASH
+ Value: 0x239
+ - Tag: DT_NULL
+ Value: 0x0
+DynamicSymbols: []
+ProgramHeaders:
+ - Type: PT_LOAD
+ FileSize: 0x23a
+ Sections:
+ - Section: .hash
+ - Section: .dynamic
+
+## Check we report a warning when the hash table goes past the end of the file.
+
+## Case A.1: the hash table ends right before the EOF. We have a broken nbucket
+## field that has a value larger than the number of buckets.
+# RUN: yaml2obj --docnum=5 %s -o %t5.1.o -DNBUCKET=0x5d -DNCHAIN=0x1
+# RUN: llvm-readelf --hash-table %t5.1.o 2>&1 | \
+# RUN: FileCheck %s --check-prefix=NOERR1 --implicit-check-not="warning:"
+# RUN: llvm-readobj --hash-table %t5.1.o 2>&1 | \
+# RUN: FileCheck %s --check-prefix=NOERR1 --implicit-check-not="warning:"
+
+# NOERR1: HashTable {
+# NOERR1-NEXT: Num Buckets: 93
+# NOERR1-NEXT: Num Chains: 1
+## Here we would dump the rest of the file as buckets array because we have a broken nbucket field.
+## No need to check what we dump, we only want to test that we have no unexpected warnings/crashes.
+# NOERR1-NEXT: Buckets:
+# NOERR1-NEXT: Chains: [0]
+# NOERR1-NEXT: }
+
+## Case A.2: the hash table ends 1 byte past the EOF. We have a broken nbucket
+## field that has a value larger than the number of buckets.
+# RUN: yaml2obj --docnum=5 %s -o %t5.2.o -DNBUCKET=0x5e -DNCHAIN=0x1
+# RUN: llvm-readelf --hash-table %t5.2.o 2>&1 | \
+# RUN: FileCheck %s --check-prefix=ERR2 -DFILE=%t5.2.o --implicit-check-not="warning:"
+# RUN: llvm-readobj --hash-table %t5.2.o 2>&1 | \
+# RUN: FileCheck %s --check-prefix=ERR2 -DFILE=%t5.2.o --implicit-check-not="warning:"
+
+# ERR2: HashTable {
+# ERR2-NEXT: warning: '[[FILE]]': the hash table at offset 0x54 goes past the end of the file (0x1d4), nbucket = 94, nchain = 1{{$}}
+# ERR2-NEXT: }
+
+## Case B.1: the hash table ends right before the EOF. We have a broken nchain
+## field that has a value larger than the number of chains.
+# RUN: yaml2obj --docnum=5 %s -o %t5.3.o -DNBUCKET=0x1 -DNCHAIN=0x5d
+# RUN: llvm-readelf --hash-table %t5.3.o 2>&1 | \
+# RUN: FileCheck %s --check-prefix=NOERR2 -DFILE=%t5.3.o --implicit-check-not="warning:"
+# RUN: llvm-readobj --hash-table %t5.3.o 2>&1 | \
+# RUN: FileCheck %s --check-prefix=NOERR2 -DFILE=%t5.3.o --implicit-check-not="warning:"
+
+# NOERR2: warning: '[[FILE]]': hash table nchain (93)
diff ers from symbol count derived from SHT_DYNSYM section header (1)
+# NOERR2: HashTable {
+# NOERR2-NEXT: Num Buckets: 1
+# NOERR2-NEXT: Num Chains: 93
+# NOERR2-NEXT: Buckets: [0]
+## Here we would dump the rest of the file as chain array because we have a broken nchain field.
+## No need to check what we dump, we only want to test that we have no unexpected warnings/crashes.
+# NOERR2-NEXT: Chains:
+# NOERR2-NEXT: }
+
+## Case B.2: the hash table ends 1 byte past the EOF. We have a broken nchain
+## field that has a value larger than the number of chains.
+# RUN: yaml2obj --docnum=5 %s -o %t5.4.o -DNBUCKET=0x1 -DNCHAIN=0x5e
+# RUN: llvm-readelf --hash-table %t5.4.o 2>&1 | \
+# RUN: FileCheck %s --check-prefix=ERR3 -DFILE=%t5.4.o --implicit-check-not="warning:"
+# RUN: llvm-readobj --hash-table %t5.4.o 2>&1 | \
+# RUN: FileCheck %s --check-prefix=ERR3 -DFILE=%t5.4.o --implicit-check-not="warning:"
+
+# ERR3: warning: '[[FILE]]': hash table nchain (94)
diff ers from symbol count derived from SHT_DYNSYM section header (1)
+# ERR3: HashTable {
+# ERR3-NEXT: warning: '[[FILE]]': the hash table at offset 0x54 goes past the end of the file (0x1d4), nbucket = 1, nchain = 94{{$}}
+# ERR3-NEXT: }
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS32
+ Data: ELFDATA2LSB
+ Type: ET_DYN
+ Machine: EM_X86_64
+Sections:
+ - Name: .hash
+ Type: SHT_HASH
+ Flags: [ SHF_ALLOC ]
+ Bucket: [ 0 ]
+ NBucket: [[NBUCKET]]
+ Chain: [ 0 ]
+ NChain: [[NCHAIN]]
+ - Name: .dynamic
+ Type: SHT_DYNAMIC
+ Flags: [ SHF_WRITE, SHF_ALLOC ]
+ Entries:
+ - Tag: DT_HASH
+ Value: 0x0
+ - Tag: DT_NULL
+ Value: 0x0
+DynamicSymbols: []
+ProgramHeaders:
+ - Type: PT_LOAD
+ Sections:
+ - Section: .hash
+ - Section: .dynamic
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 6d785afd6530..ca05f99aa715 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -2607,9 +2607,35 @@ template <class ELFT> void ELFDumper<ELFT>::printNeededLibraries() {
W.startLine() << L << "\n";
}
+template <class ELFT>
+static bool checkHashTable(const ELFFile<ELFT> *Obj,
+ const typename ELFT::Hash *H, StringRef FileName) {
+ auto WarnAndReturn = [&](uint64_t Off, const Twine &Msg = "") {
+ reportWarning(createError("the hash table at offset 0x" +
+ Twine::utohexstr(Off) +
+ " goes past the end of the file (0x" +
+ Twine::utohexstr(Obj->getBufSize()) + ")" + Msg),
+ FileName);
+ return false;
+ };
+
+ // Each SHT_HASH section starts from two 32-bit fields: nbucket and nchain.
+ const unsigned HeaderSize = 2 * sizeof(typename ELFT::Word);
+ const uint64_t SecOffset = (const uint8_t *)H - Obj->base();
+ if (Obj->getBufSize() - SecOffset < HeaderSize)
+ return WarnAndReturn(SecOffset);
+
+ if (Obj->getBufSize() - SecOffset - HeaderSize <
+ ((uint64_t)H->nbucket + H->nchain) * sizeof(typename ELFT::Word))
+ return WarnAndReturn(SecOffset, ", nbucket = " + Twine(H->nbucket) +
+ ", nchain = " + Twine(H->nchain));
+ return true;
+}
+
template <typename ELFT> void ELFDumper<ELFT>::printHashTable() {
DictScope D(W, "HashTable");
- if (!HashTable)
+ if (!HashTable ||
+ !checkHashTable(ObjF->getELFFile(), HashTable, ObjF->getFileName()))
return;
W.printNumber("Num Buckets", HashTable->nbucket);
W.printNumber("Num Chains", HashTable->nchain);
@@ -3899,9 +3925,7 @@ template <class ELFT> void GNUStyle<ELFT>::printHashSymbols(const ELFO *Obj) {
auto StringTable = this->dumper()->getDynamicStringTable();
auto DynSyms = this->dumper()->dynamic_symbols();
- // Try printing .hash
- if (auto SysVHash = this->dumper()->getHashTable()) {
- OS << "\n Symbol table of .hash for image:\n";
+ auto PrintHashTable = [&](const Elf_Hash *SysVHash) {
if (ELFT::Is64Bits)
OS << " Num Buc: Value Size Type Bind Vis Ndx Name";
else
@@ -3930,6 +3954,12 @@ template <class ELFT> void GNUStyle<ELFT>::printHashSymbols(const ELFO *Obj) {
Visited[Ch] = true;
}
}
+ };
+
+ if (const Elf_Hash *SysVHash = this->dumper()->getHashTable()) {
+ OS << "\n Symbol table of .hash for image:\n";
+ if (checkHashTable(Obj, SysVHash, this->FileName))
+ PrintHashTable(SysVHash);
}
// Try printing .gnu.hash
@@ -4452,6 +4482,9 @@ template <class ELFT>
void GNUStyle<ELFT>::printHashHistogram(const ELFFile<ELFT> *Obj) {
// Print histogram for .hash section
if (const Elf_Hash *HashTable = this->dumper()->getHashTable()) {
+ if (!checkHashTable(Obj, HashTable, this->FileName))
+ return;
+
size_t NBucket = HashTable->nbucket;
size_t NChain = HashTable->nchain;
ArrayRef<Elf_Word> Buckets = HashTable->buckets();
More information about the llvm-commits
mailing list