[llvm-readobj] - Fix a crash scenario in GNUStyle<ELFT>::printHashSymbols().

We might crash when the dynamic symbols table is empty (or not found)
and --hash-symbols is requested. Both .hash and .gnu.hash logic is affected.

The patch fixes this issue.

Differential revision: https://reviews.llvm.org/D83037
This commit is contained in:
Georgii Rymar 2020-07-02 13:38:42 +03:00
parent cd503166fb
commit d5cbf7ba32
2 changed files with 151 additions and 3 deletions

View File

@ -509,3 +509,131 @@ ProgramHeaders:
Sections:
- Section: .gnu.hash
- Section: .dynamic
## Check the behavior when the dynamic symbol table is empty or not found.
## Case A.1: Check we report a warning when the dynamic symbol table is empty and we attempt to print hash symbols
## from the .hash table. The number of symbols in the dynamic symbol table can be calculated from its size
## or derived from the Chain vector of the .hash table. Make both ways to return a zero to do the check.
# RUN: yaml2obj --docnum=9 %s -o %t9.1.so
# RUN: llvm-readelf --hash-symbols %t9.1.so 2>&1 | FileCheck %s -DFILE=%t9.1.so --check-prefix=DYNSYM-EMPTY-HASH
# DYNSYM-EMPTY-HASH: Symbol table of .hash for image:
# DYNSYM-EMPTY-HASH-NEXT: Num Buc: Value Size Type Bind Vis Ndx Name
# DYNSYM-EMPTY-HASH-NEXT: warning: '[[FILE]]': unable to print symbols for the .hash table: the dynamic symbol table is empty
# DYNSYM-EMPTY-HASH-NOT: {{.}}
--- !ELF
FileHeader:
Class: ELFCLASS32
Data: ELFDATA2LSB
Type: ET_DYN
Machine: EM_386
Sections:
- Name: .hash
Type: SHT_HASH
Flags: [ SHF_ALLOC ]
Bucket: [ 0 ]
Chain: [ ]
- Name: .dynamic
Type: SHT_DYNAMIC
Flags: [ SHF_ALLOC ]
Entries:
- Tag: DT_HASH
Value: 0x0
- Tag: DT_STRTAB
## PT_LOAD p_offset == .hash offset == 0x54.
## 0x54 + 0x2c == 0x80 == .dynstr offset.
Value: 0x2c
- Tag: DT_STRSZ
Value: 0x1
- Tag: DT_NULL
Value: 0x0
- Name: .dynstr
Type: SHT_STRTAB
Flags: [ SHF_ALLOC ]
- Name: .dynsym
Type: [[DYNSYMTYPE=SHT_DYNSYM]]
Flags: [ SHF_ALLOC ]
Size: 0
ProgramHeaders:
- Type: PT_LOAD
Flags: [ PF_R, PF_X ]
Sections:
- Section: .hash
- Section: .dynamic
- Section: .dynstr
## Case A.2: similar to A.1, but now check that we report a warning when the dynamic symbol table was not found.
## To do that, set the type of the .dynsym to SHT_PROGBITS to hide it.
# RUN: yaml2obj --docnum=9 -DDYNSYMTYPE=SHT_PROGBITS %s -o %t9.2.so
# RUN: llvm-readelf --hash-symbols %t9.2.so 2>&1 | FileCheck %s -DFILE=%t9.2.so --check-prefix=DYNSYM-NOTFOUND-HASH
# DYNSYM-NOTFOUND-HASH: Symbol table of .hash for image:
# DYNSYM-NOTFOUND-HASH-NEXT: Num Buc: Value Size Type Bind Vis Ndx Name
# DYNSYM-NOTFOUND-HASH-NEXT: warning: '[[FILE]]': unable to print symbols for the .hash table: the dynamic symbol table was not found
# DYNSYM-NOTFOUND-HASH-NOT: {{.}}
## Case B.1: Check we report a warning when the dynamic symbol table is empty and we attempt to print
## hash symbols from the .gnu.hash table.
# RUN: yaml2obj --docnum=10 %s -o %t10.1.so
# RUN: llvm-readelf --hash-symbols %t10.1.so 2>&1 | FileCheck %s -DFILE=%t10.1.so --check-prefix=DYNSYM-EMPTY-GNUHASH
# DYNSYM-EMPTY-GNUHASH: Symbol table of .gnu.hash for image:
# DYNSYM-EMPTY-GNUHASH-NEXT: Num Buc: Value Size Type Bind Vis Ndx Name
# DYNSYM-EMPTY-GNUHASH-NEXT: warning: '[[FILE]]': unable to print symbols for the .gnu.hash table: the dynamic symbol table is empty
# DYNSYM-EMPTY-GNUHASH-NOT: {{.}}
## Case B.2: similar to B.1, but now check that we report a warning when the dynamic symbol table was not found.
## To do that, set the type of the .dynsym to SHT_PROGBITS to hide it.
# RUN: yaml2obj --docnum=10 -DDYNSYMTYPE=SHT_PROGBITS %s -o %t10.2.so
# RUN: llvm-readelf --hash-symbols %t10.2.so 2>&1 | FileCheck %s -DFILE=%t10.2.so --check-prefix=DYNSYM-NOTFOUND-GNUHASH
# DYNSYM-NOTFOUND-GNUHASH: Symbol table of .gnu.hash for image:
# DYNSYM-NOTFOUND-GNUHASH-NEXT: Num Buc: Value Size Type Bind Vis Ndx Name
# DYNSYM-NOTFOUND-GNUHASH-NEXT: warning: '[[FILE]]': unable to print symbols for the .gnu.hash table: the dynamic symbol table was not found
# DYNSYM-NOTFOUND-GNUHASH-NOT: {{.}}
--- !ELF
FileHeader:
Class: ELFCLASS32
Data: ELFDATA2LSB
Type: ET_DYN
Machine: EM_386
Sections:
- Name: .gnu.hash
Type: SHT_GNU_HASH
Flags: [ SHF_ALLOC ]
Header:
SymNdx: 0x0
Shift2: 0x0
BloomFilter: [ 0x0 ]
HashBuckets: [ 0x1 ]
HashValues: [ 0x0 ]
- Name: .dynamic
Type: SHT_DYNAMIC
Flags: [ SHF_ALLOC ]
Entries:
- Tag: DT_GNU_HASH
Value: 0x0
- Tag: DT_STRTAB
## PT_LOAD p_offset == .hash offset == 0x54.
## 0x54 + 0x3c == 0x80 == .dynstr offset.
Value: 0x3c
- Tag: DT_STRSZ
Value: 0x1
- Tag: DT_NULL
Value: 0x0
- Name: .dynstr
Type: SHT_STRTAB
- Name: .dynsym
Type: [[DYNSYMTYPE=SHT_DYNSYM]]
Flags: [ SHF_ALLOC ]
Size: 0
ProgramHeaders:
- Type: PT_LOAD
Flags: [ PF_R, PF_X ]
Sections:
- Section: .gnu.hash
- Section: .dynamic
- Section: .dynstr

View File

@ -4066,7 +4066,7 @@ template <class ELFT> void GNUStyle<ELFT>::printHashSymbols(const ELFO *Obj) {
if (this->dumper()->getDynamicStringTable().empty())
return;
auto StringTable = this->dumper()->getDynamicStringTable();
auto DynSyms = this->dumper()->dynamic_symbols();
Elf_Sym_Range DynSyms = this->dumper()->dynamic_symbols();
auto PrintHashTable = [&](const Elf_Hash *SysVHash) {
if (ELFT::Is64Bits)
@ -4075,6 +4075,16 @@ template <class ELFT> void GNUStyle<ELFT>::printHashSymbols(const ELFO *Obj) {
OS << " Num Buc: Value Size Type Bind Vis Ndx Name";
OS << "\n";
const Elf_Sym *FirstSym = DynSyms.empty() ? nullptr : &DynSyms[0];
if (!FirstSym) {
Optional<DynRegionInfo> DynSymRegion = this->dumper()->getDynSymRegion();
this->reportUniqueWarning(
createError(Twine("unable to print symbols for the .hash table: the "
"dynamic symbol table ") +
(DynSymRegion ? "is empty" : "was not found")));
return;
}
auto Buckets = SysVHash->buckets();
auto Chains = SysVHash->chains();
for (uint32_t Buc = 0; Buc < SysVHash->nbucket; Buc++) {
@ -4093,7 +4103,7 @@ template <class ELFT> void GNUStyle<ELFT>::printHashSymbols(const ELFO *Obj) {
break;
}
printHashedSymbol(Obj, &DynSyms[0], Ch, StringTable, Buc);
printHashedSymbol(Obj, FirstSym, Ch, StringTable, Buc);
Visited[Ch] = true;
}
}
@ -4124,6 +4134,16 @@ template <class ELFT> void GNUStyle<ELFT>::printHashSymbols(const ELFO *Obj) {
return;
}
const Elf_Sym *FirstSym = DynSyms.empty() ? nullptr : &DynSyms[0];
if (!FirstSym) {
Optional<DynRegionInfo> DynSymRegion = this->dumper()->getDynSymRegion();
this->reportUniqueWarning(createError(
Twine("unable to print symbols for the .gnu.hash table: the "
"dynamic symbol table ") +
(DynSymRegion ? "is empty" : "was not found")));
return;
}
auto Buckets = GnuHash->buckets();
for (uint32_t Buc = 0; Buc < GnuHash->nbuckets; Buc++) {
if (Buckets[Buc] == ELF::STN_UNDEF)
@ -4132,7 +4152,7 @@ template <class ELFT> void GNUStyle<ELFT>::printHashSymbols(const ELFO *Obj) {
uint32_t GnuHashable = Index - GnuHash->symndx;
// Print whole chain
while (true) {
printHashedSymbol(Obj, &DynSyms[0], Index++, StringTable, Buc);
printHashedSymbol(Obj, FirstSym, Index++, StringTable, Buc);
// Chain ends at symbol with stopper bit
if ((GnuHash->values(DynSyms.size())[GnuHashable++] & 1) == 1)
break;