Index: llvm/test/tools/llvm-readobj/ELF/hash-symbols.test =================================================================== --- llvm/test/tools/llvm-readobj/ELF/hash-symbols.test +++ llvm/test/tools/llvm-readobj/ELF/hash-symbols.test @@ -655,3 +655,76 @@ - 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. + +## Case A: a hashed symbol can't be read, because of reading past the EOF attempt. +# RUN: yaml2obj --docnum=11 -DVALUE=0x17 %s -o %t11.past.eof.so +# RUN: llvm-readelf --hash-symbols %t11.past.eof.so 2>&1 | \ +# RUN: FileCheck %s -DFILE=%t11.past.eof.so --check-prefix=BUCKET-PAST-EOF + +# BUCKET-PAST-EOF: Symbol table of .gnu.hash for image: +# BUCKET-PAST-EOF-NEXT: Num Buc: Value Size Type Bind Vis Ndx Name +# BUCKET-PAST-EOF-NEXT: warning: '[[FILE]]': unable to print hashed symbol with index 23: reading from 0xc0 goes past the end of the file (0x2f0) +# BUCKET-PAST-EOF-NOT: {{.}} + +## Case B.1: a hash value entry can't be read because we have a broken value in the hash buckets array and trying to read +## a data past the end of the hash values array. The VALUE used is equal to the value from the case A minus 1. +# RUN: yaml2obj --docnum=11 -DVALUE=0x16 %s -o %t11.value1.so +# RUN: llvm-readelf --hash-symbols %t11.value1.so 2>&1 | \ +# RUN: FileCheck %s -DFILE=%t11.value1.so --check-prefix=BUCKET-READ-VALUE-A + +# BUCKET-READ-VALUE-A: Num Buc: Value Size Type Bind Vis Ndx Name +## Note: we are trying to dump a dynamic symbol using a wrong index and so reading some arbitrary data as symbol data. +## That is why the following warning is reported. +# BUCKET-READ-VALUE-A-NEXT: warning: '[[FILE]]': st_name (0x36) is past the end of the string table of size 0x5 +# BUCKET-READ-VALUE-A-NEXT: 22 1: 0000000000000000 1 NOTYPE LOCAL DEFAULT UND +# BUCKET-READ-VALUE-A-NEXT: warning: '[[FILE]]': unable to read the hash value: index (22) goes past the end of the hash values array of size 2 + +## Case B.2: the same as B.1, but now the value is equal to the size of the hash values array. +# RUN: yaml2obj --docnum=11 -DVALUE=0x2 %s -o %t11.value2.so +# RUN: llvm-readelf --hash-symbols %t11.value2.so 2>&1 | \ +# RUN: FileCheck %s -DFILE=%t11.value2.so --check-prefix=BUCKET-READ-VALUE-B + +# BUCKET-READ-VALUE-B: Num Buc: Value Size Type Bind Vis Ndx Name +## Note: we are trying to dump a dynamic symbol using a wrong index and so reading some arbitrary data as symbol data. +## That is why the following warning is reported. +# BUCKET-READ-VALUE-B-NEXT: warning: '[[FILE]]': st_name (0x6f6f6600) is past the end of the string table of size 0x5 +# BUCKET-READ-VALUE-B-NEXT: 2 1: 2e007274736e7964 3314769694139775332 NOTYPE LOCAL DEFAULT 11776 +# BUCKET-READ-VALUE-B-NEXT: warning: '[[FILE]]': unable to read the hash value: index (2) goes past the end of the hash values array of size 2 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN +Sections: + - Name: .gnu.hash + Type: SHT_GNU_HASH + Flags: [ SHF_ALLOC ] + Link: .dynsym + Header: + SymNdx: 0x0 + Shift2: 0x0 + BloomFilter: [ 0x0 ] + HashBuckets: [ 0x0, [[VALUE]] ] + 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 Index: llvm/tools/llvm-readobj/ELFDumper.cpp =================================================================== --- llvm/tools/llvm-readobj/ELFDumper.cpp +++ llvm/tools/llvm-readobj/ELFDumper.cpp @@ -4053,6 +4053,19 @@ return; } + auto GetSymbol = [&](uint64_t SymIndex) -> const Elf_Sym * { + uint64_t SymOffset = + reinterpret_cast(FirstSym) - this->Obj.base(); + if (SymOffset + (SymIndex + 1) * sizeof(Elf_Sym) <= this->Obj.getBufSize()) + return FirstSym + SymIndex; + this->reportUniqueWarning(createError( + "unable to print hashed symbol with index " + Twine(SymIndex) + + ": reading from 0x" + Twine::utohexstr(SymOffset) + + " goes past the end of the file (0x" + + Twine::utohexstr(this->Obj.getBufSize()) + ")")); + return nullptr; + }; + ArrayRef Buckets = GnuHash.buckets(); for (uint32_t Buc = 0; Buc < GnuHash.nbuckets; Buc++) { if (Buckets[Buc] == ELF::STN_UNDEF) @@ -4062,9 +4075,22 @@ // 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)) + printHashedSymbol(Sym, SymIndex, StringTable, Buc); + else + break; + + // Chain ends at symbol with stopper bit. + ArrayRef Values = GnuHash.values(DynSyms.size()); + if (GnuHashable >= Values.size()) { + this->reportUniqueWarning(createError( + "unable to read the hash value: index (" + Twine(GnuHashable) + + ") goes past the end of the hash values array of size " + + Twine(Values.size()))); + break; + } + + if ((Values[GnuHashable++] & 1) == 1) break; } }