From 2c089a8181bd7d2de74933fba7883a3ba5facfaf Mon Sep 17 00:00:00 2001 From: Georgii Rymar Date: Wed, 24 Jun 2020 13:23:04 +0300 Subject: [PATCH] [llvm-readobj] - Don't crash when a broken GNU hash table is dumped with --hash-symbols. Start using the `checkGNUHashTable` helper which was recently introduced to report a proper warning when a GNU hash table goes past the end of the file. Differential revision: https://reviews.llvm.org/D82449 --- test/tools/llvm-readobj/ELF/hash-symbols.test | 61 +++++++++++++++++++ tools/llvm-readobj/ELFDumper.cpp | 50 ++++++++------- 2 files changed, 90 insertions(+), 21 deletions(-) diff --git a/test/tools/llvm-readobj/ELF/hash-symbols.test b/test/tools/llvm-readobj/ELF/hash-symbols.test index dd0d9e39817..edd3c1e9b84 100644 --- a/test/tools/llvm-readobj/ELF/hash-symbols.test +++ b/test/tools/llvm-readobj/ELF/hash-symbols.test @@ -448,3 +448,64 @@ ProgramHeaders: Sections: - Section: .hash - Section: .dynamic + +## Check we report a proper warning when a GNU 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=8 -D MASKWORDS=4294967295 -D NBUCKETS=3 %s -o %t.err.maskwords +# RUN: llvm-readelf --hash-symbols %t.err.maskwords 2>&1 | \ +# RUN: FileCheck %s -DFILE=%t.err.maskwords -DMASKWORDS=4294967295 -DNBUCKETS=3 --check-prefix=ERR4 + +# ERR4: Symbol table of .gnu.hash for image: +# ERR4-NEXT: Num Buc: Value Size Type Bind Vis Ndx Name +# ERR4-NEXT: 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 table goes past the end of the file. +# RUN: yaml2obj --docnum=8 -D MASKWORDS=2 -D NBUCKETS=4294967295 %s -o %t.err.nbuckets +# RUN: llvm-readelf --hash-symbols %t.err.nbuckets 2>&1 | \ +# RUN: FileCheck %s -DFILE=%t.err.nbuckets -DMASKWORDS=2 -DNBUCKETS=4294967295 --check-prefix=ERR4 + +--- !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: 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] + - Name: .dynamic + Type: SHT_DYNAMIC + Flags: [ SHF_ALLOC ] + Link: .dynstr + Entries: + - Tag: DT_GNU_HASH + Value: 0x0 + - Tag: DT_NULL + Value: 0x0 +DynamicSymbols: + - Name: aaa + Binding: STB_GLOBAL + - Name: bbb + Binding: STB_GLOBAL + - Name: ccc + Binding: STB_GLOBAL + - Name: ddd + Binding: STB_GLOBAL +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_R, PF_X ] + Sections: + - Section: .gnu.hash + - Section: .dynamic diff --git a/tools/llvm-readobj/ELFDumper.cpp b/tools/llvm-readobj/ELFDumper.cpp index e38ae9d56fb..b424918a8d4 100644 --- a/tools/llvm-readobj/ELFDumper.cpp +++ b/tools/llvm-readobj/ELFDumper.cpp @@ -4069,27 +4069,35 @@ template void GNUStyle::printHashSymbols(const ELFO *Obj) { PrintHashTable(SysVHash); } - // Try printing .gnu.hash - if (auto GnuHash = this->dumper()->getGnuHashTable()) { - OS << "\n Symbol table of .gnu.hash for image:\n"; - if (ELFT::Is64Bits) - OS << " Num Buc: Value Size Type Bind Vis Ndx Name"; - else - OS << " Num Buc: Value Size Type Bind Vis Ndx Name"; - OS << "\n"; - auto Buckets = GnuHash->buckets(); - for (uint32_t Buc = 0; Buc < GnuHash->nbuckets; Buc++) { - if (Buckets[Buc] == ELF::STN_UNDEF) - continue; - uint32_t Index = Buckets[Buc]; - uint32_t GnuHashable = Index - GnuHash->symndx; - // Print whole chain - while (true) { - printHashedSymbol(Obj, &DynSyms[0], Index++, StringTable, Buc); - // Chain ends at symbol with stopper bit - if ((GnuHash->values(DynSyms.size())[GnuHashable++] & 1) == 1) - break; - } + // Try printing the .gnu.hash table. + const Elf_GnuHash *GnuHash = this->dumper()->getGnuHashTable(); + if (!GnuHash) + return; + + OS << "\n Symbol table of .gnu.hash for image:\n"; + if (ELFT::Is64Bits) + OS << " Num Buc: Value Size Type Bind Vis Ndx Name"; + else + OS << " Num Buc: Value Size Type Bind Vis Ndx Name"; + OS << "\n"; + + if (Error E = checkGNUHashTable(Obj, GnuHash)) { + this->reportUniqueWarning(std::move(E)); + return; + } + + auto Buckets = GnuHash->buckets(); + for (uint32_t Buc = 0; Buc < GnuHash->nbuckets; Buc++) { + if (Buckets[Buc] == ELF::STN_UNDEF) + continue; + uint32_t Index = Buckets[Buc]; + uint32_t GnuHashable = Index - GnuHash->symndx; + // Print whole chain + while (true) { + printHashedSymbol(Obj, &DynSyms[0], Index++, StringTable, Buc); + // Chain ends at symbol with stopper bit + if ((GnuHash->values(DynSyms.size())[GnuHashable++] & 1) == 1) + break; } } }