forked from OSchip/llvm-project
[llvm-readobj] - Fix possible crashes related to dumping gnu hash symbols.
It fixes possible scenarios when we crash/assert with `--hash-symbols` when dumping an invalid GNU hash table which has a broken value in the buckets array. This fixes a crash reported in comments for https://bugs.llvm.org/show_bug.cgi?id=47681 Differential revision: https://reviews.llvm.org/D88561
This commit is contained in:
parent
30e6033b45
commit
80bf29f00c
|
@ -125,7 +125,7 @@ ProgramHeaders:
|
|||
# SYMNDX-NEXT: Shift Count: 0
|
||||
# SYMNDX-NEXT: Bloom Filter: [0x1]
|
||||
# SYMNDX-NEXT: Buckets: [2]
|
||||
# SYMNDX-NEXT: warning: '[[FILE]]': unable to dump 'Values' for the SHT_GNU_HASH section: the first hashed symbol index (2) is larger than the number of dynamic symbols (2)
|
||||
# SYMNDX-NEXT: warning: '[[FILE]]': unable to dump 'Values' for the SHT_GNU_HASH section: the first hashed symbol index (2) is greater than or equal to the number of dynamic symbols (2)
|
||||
# SYMNDX-NEXT: }
|
||||
|
||||
--- !ELF
|
||||
|
|
|
@ -331,7 +331,7 @@ ProgramHeaders:
|
|||
# RUN: llvm-readelf --elf-hash-histogram %t9 2>&1 | \
|
||||
# RUN: FileCheck %s -DFILE=%t9 --check-prefix=ERR6
|
||||
|
||||
# ERR6: warning: '[[FILE]]': unable to print the GNU hash table histogram: the first hashed symbol index (16) is larger than the number of dynamic symbols (1)
|
||||
# ERR6: warning: '[[FILE]]': unable to print the GNU hash table histogram: the first hashed symbol index (16) is greater than or equal to the number of dynamic symbols (1)
|
||||
|
||||
## Case B: we do not report a warning when the buckets array contains only zero values.
|
||||
# RUN: yaml2obj --docnum=7 -DVAL=0x0 %s -o %t10
|
||||
|
|
|
@ -655,3 +655,81 @@ ProgramHeaders:
|
|||
- Section: .gnu.hash
|
||||
- Section: .dynamic
|
||||
- Section: .dynstr
|
||||
|
||||
## In this case we have a broken value in the hash buckets array. Normally it contains an
|
||||
## index into the dynamic symbol table and also is used to get a hash value from the hash values array.
|
||||
## llvm-readelf attempts to read a symbol that is past the end of the dynamic symbol table.
|
||||
|
||||
# RUN: yaml2obj --docnum=11 -DVALUE=0x2 %s -o %t11.past.dynsym.so
|
||||
# RUN: llvm-readelf --hash-symbols %t11.past.dynsym.so 2>&1 | \
|
||||
# RUN: FileCheck %s -DFILE=%t11.past.dynsym.so --check-prefix=BUCKET-PAST-DYNSYM
|
||||
|
||||
# BUCKET-PAST-DYNSYM: Symbol table of .gnu.hash for image:
|
||||
# BUCKET-PAST-DYNSYM-NEXT: Num Buc: Value Size Type Bind Vis Ndx Name
|
||||
# BUCKET-PAST-DYNSYM-NEXT: warning: '[[FILE]]': unable to print hashed symbol with index 2, which is greater than or equal to the number of dynamic symbols (2)
|
||||
# BUCKET-PAST-DYNSYM-NEXT: 1 2: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND foo
|
||||
# BUCKET-PAST-DYNSYM-NOT: {{.}}
|
||||
|
||||
--- !ELF
|
||||
FileHeader:
|
||||
Class: ELFCLASS64
|
||||
Data: ELFDATA2LSB
|
||||
Type: ET_DYN
|
||||
Sections:
|
||||
- Name: .gnu.hash
|
||||
Type: SHT_GNU_HASH
|
||||
Flags: [ SHF_ALLOC ]
|
||||
Link: .dynsym
|
||||
Header:
|
||||
SymNdx: [[SYMNDX=0x0]]
|
||||
Shift2: 0x0
|
||||
BloomFilter: [ 0x0 ]
|
||||
HashBuckets: [ 0x0, [[VALUE]], 0x1 ]
|
||||
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:
|
||||
- Name: foo
|
||||
Binding: STB_GLOBAL
|
||||
ProgramHeaders:
|
||||
- Type: PT_LOAD
|
||||
Flags: [ PF_R, PF_X ]
|
||||
Sections:
|
||||
- Section: .gnu.hash
|
||||
- Section: .dynamic
|
||||
|
||||
## In this case we are unable to read a hash value for a symbol with
|
||||
## an index that is less than the index of the first hashed symbol.
|
||||
|
||||
# RUN: yaml2obj --docnum=11 -DSYMNDX=0x2 -DVALUE=0x1 %s -o %t11.first.hashed.so
|
||||
# RUN: llvm-readelf --hash-symbols %t11.first.hashed.so 2>&1 | \
|
||||
# RUN: FileCheck %s -DFILE=%t11.first.hashed.so --check-prefix=FIRST-HASHED
|
||||
|
||||
# FIRST-HASHED: Symbol table of .gnu.hash for image:
|
||||
# FIRST-HASHED-NEXT: Num Buc: Value Size Type Bind Vis Ndx Name
|
||||
# FIRST-HASHED-NEXT: warning: '[[FILE]]': unable to get hash values for the SHT_GNU_HASH section: the first hashed symbol index (2) is greater than or equal to the number of dynamic symbols (2)
|
||||
# FIRST-HASHED-NEXT: 1 1: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND foo
|
||||
# FIRST-HASHED-NEXT: warning: '[[FILE]]': unable to read the hash value for symbol with index 1, which is less than the index of the first hashed symbol (2)
|
||||
# FIRST-HASHED-NEXT: 1 2: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND foo
|
||||
# FIRST-HASHED-NOT: {{.}}
|
||||
|
||||
## In this case one of the chain values doesn't end with a stopper bit and llvm-readelf attempts to read
|
||||
## a dynamic symbol with an index that is equal to the number of dynamic symbols.
|
||||
|
||||
# RUN: yaml2obj --docnum=11 -DSYMNDX=0x1 -DVALUE=0x1 %s -o %t11.chain.bit.so
|
||||
# RUN: llvm-readelf --hash-symbols %t11.chain.bit.so 2>&1 | \
|
||||
# RUN: FileCheck %s -DFILE=%t11.chain.bit.so --check-prefix=CHAIN-BIT
|
||||
|
||||
# CHAIN-BIT: Symbol table of .gnu.hash for image:
|
||||
# CHAIN-BIT-NEXT: Num Buc: Value Size Type Bind Vis Ndx Name
|
||||
# CHAIN-BIT-NEXT: 1 1: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND foo
|
||||
# CHAIN-BIT-NEXT: warning: '[[FILE]]': unable to print hashed symbol with index 2, which is greater than or equal to the number of dynamic symbols (2)
|
||||
# CHAIN-BIT-NEXT: 1 2: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND foo
|
||||
# CHAIN-BIT-NOT: {{.}}
|
||||
|
|
|
@ -2743,10 +2743,10 @@ getGnuHashTableChains(Optional<DynRegionInfo> DynSymRegion,
|
|||
// is irrelevant and we should not report a warning.
|
||||
ArrayRef<typename ELFT::Word> Buckets = GnuHashTable->buckets();
|
||||
if (!llvm::all_of(Buckets, [](typename ELFT::Word V) { return V == 0; }))
|
||||
return createError("the first hashed symbol index (" +
|
||||
Twine(GnuHashTable->symndx) +
|
||||
") is larger than the number of dynamic symbols (" +
|
||||
Twine(NumSyms) + ")");
|
||||
return createError(
|
||||
"the first hashed symbol index (" + Twine(GnuHashTable->symndx) +
|
||||
") is greater than or equal to the number of dynamic symbols (" +
|
||||
Twine(NumSyms) + ")");
|
||||
// There is no way to represent an array of (dynamic symbols count - symndx)
|
||||
// length.
|
||||
return ArrayRef<typename ELFT::Word>();
|
||||
|
@ -4044,8 +4044,8 @@ void GNUStyle<ELFT>::printGnuHashTableSymbols(const Elf_GnuHash &GnuHash) {
|
|||
|
||||
Elf_Sym_Range DynSyms = this->dumper().dynamic_symbols();
|
||||
const Elf_Sym *FirstSym = DynSyms.empty() ? nullptr : &DynSyms[0];
|
||||
Optional<DynRegionInfo> DynSymRegion = this->dumper().getDynSymRegion();
|
||||
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 ") +
|
||||
|
@ -4053,18 +4053,54 @@ void GNUStyle<ELFT>::printGnuHashTableSymbols(const Elf_GnuHash &GnuHash) {
|
|||
return;
|
||||
}
|
||||
|
||||
auto GetSymbol = [&](uint64_t SymIndex,
|
||||
uint64_t SymsTotal) -> const Elf_Sym * {
|
||||
if (SymIndex >= SymsTotal) {
|
||||
this->reportUniqueWarning(createError(
|
||||
"unable to print hashed symbol with index " + Twine(SymIndex) +
|
||||
", which is greater than or equal to the number of dynamic symbols "
|
||||
"(" +
|
||||
Twine::utohexstr(SymsTotal) + ")"));
|
||||
return nullptr;
|
||||
}
|
||||
return FirstSym + SymIndex;
|
||||
};
|
||||
|
||||
Expected<ArrayRef<Elf_Word>> ValuesOrErr =
|
||||
getGnuHashTableChains<ELFT>(DynSymRegion, &GnuHash);
|
||||
ArrayRef<Elf_Word> Values;
|
||||
if (!ValuesOrErr)
|
||||
this->reportUniqueWarning(
|
||||
createError("unable to get hash values for the SHT_GNU_HASH "
|
||||
"section: " +
|
||||
toString(ValuesOrErr.takeError())));
|
||||
else
|
||||
Values = *ValuesOrErr;
|
||||
|
||||
ArrayRef<Elf_Word> 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
|
||||
// Print whole chain.
|
||||
while (true) {
|
||||
uint32_t SymIndex = Index++;
|
||||
printHashedSymbol(FirstSym + SymIndex, SymIndex, StringTable, Buc);
|
||||
// Chain ends at symbol with stopper bit
|
||||
if ((GnuHash.values(DynSyms.size())[GnuHashable++] & 1) == 1)
|
||||
if (const Elf_Sym *Sym = GetSymbol(SymIndex, DynSyms.size()))
|
||||
printHashedSymbol(Sym, SymIndex, StringTable, Buc);
|
||||
else
|
||||
break;
|
||||
|
||||
if (SymIndex < GnuHash.symndx) {
|
||||
this->reportUniqueWarning(createError(
|
||||
"unable to read the hash value for symbol with index " +
|
||||
Twine(SymIndex) +
|
||||
", which is less than the index of the first hashed symbol (" +
|
||||
Twine(GnuHash.symndx) + ")"));
|
||||
break;
|
||||
}
|
||||
|
||||
// Chain ends at symbol with stopper bit.
|
||||
if ((Values[SymIndex - GnuHash.symndx] & 1) == 1)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue