mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2025-01-31 20:51:52 +01:00
[llvm-readobj] - Do not crash when an invalid .eh_frame_hdr is dumped using --unwind.
When the p_offset/p_filesz of the PT_GNU_EH_FRAME is invalid (e.g larger than the file size) then llvm-readobj might crash. This patch fixes the issue. I've introduced `ELFFile<ELFT>::getSegmentContent` method, which is very similar to `ELFFile<ELFT>::getSectionContentsAsArray` one. Differential revision: https://reviews.llvm.org/D80380
This commit is contained in:
parent
f14df40a71
commit
dc0839500a
@ -64,6 +64,17 @@ std::string getSecIndexForError(const ELFFile<ELFT> *Obj,
|
||||
return "[unknown index]";
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
std::string getPhdrIndexForError(const ELFFile<ELFT> *Obj,
|
||||
const typename ELFT::Phdr *Phdr) {
|
||||
auto Headers = Obj->program_headers();
|
||||
if (Headers)
|
||||
return ("[index " + Twine(Phdr - &Headers->front()) + "]").str();
|
||||
// See comment in the getSecIndexForError() above.
|
||||
llvm::consumeError(Headers.takeError());
|
||||
return "[unknown index]";
|
||||
}
|
||||
|
||||
static inline Error defaultWarningHandler(const Twine &Msg) {
|
||||
return createError(Msg);
|
||||
}
|
||||
@ -299,6 +310,7 @@ public:
|
||||
template <typename T>
|
||||
Expected<ArrayRef<T>> getSectionContentsAsArray(const Elf_Shdr *Sec) const;
|
||||
Expected<ArrayRef<uint8_t>> getSectionContents(const Elf_Shdr *Sec) const;
|
||||
Expected<ArrayRef<uint8_t>> getSegmentContents(const Elf_Phdr *Phdr) const;
|
||||
};
|
||||
|
||||
using ELF32LEFile = ELFFile<ELF32LE>;
|
||||
@ -422,6 +434,26 @@ ELFFile<ELFT>::getSectionContentsAsArray(const Elf_Shdr *Sec) const {
|
||||
return makeArrayRef(Start, Size / sizeof(T));
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
Expected<ArrayRef<uint8_t>>
|
||||
ELFFile<ELFT>::getSegmentContents(const Elf_Phdr *Phdr) const {
|
||||
uintX_t Offset = Phdr->p_offset;
|
||||
uintX_t Size = Phdr->p_filesz;
|
||||
|
||||
if (std::numeric_limits<uintX_t>::max() - Offset < Size)
|
||||
return createError("program header " + getPhdrIndexForError(this, Phdr) +
|
||||
" has a p_offset (0x" + Twine::utohexstr(Offset) +
|
||||
") + p_filesz (0x" + Twine::utohexstr(Size) +
|
||||
") that cannot be represented");
|
||||
if (Offset + Size > Buf.size())
|
||||
return createError("program header " + getPhdrIndexForError(this, Phdr) +
|
||||
" has a p_offset (0x" + Twine::utohexstr(Offset) +
|
||||
") + p_filesz (0x" + Twine::utohexstr(Size) +
|
||||
") that is greater than the file size (0x" +
|
||||
Twine::utohexstr(Buf.size()) + ")");
|
||||
return makeArrayRef(base() + Offset, Size);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
Expected<ArrayRef<uint8_t>>
|
||||
ELFFile<ELFT>::getSectionContents(const Elf_Shdr *Sec) const {
|
||||
|
@ -262,3 +262,64 @@ Sections:
|
||||
Type: SHT_PROGBITS
|
||||
## Length is set to 0xFF, though the actual section length is 4.
|
||||
Content: "FF000000"
|
||||
|
||||
## Check we report an error when we can't read the content of the .eh_frame section.
|
||||
|
||||
## Case A: test we report an error when the p_offset of the PT_GNU_EH_FRAME
|
||||
## is invalid (goes past the end of the file).
|
||||
|
||||
# RUN: yaml2obj --docnum=4 %s -o %t4 -DOFFSET=0xffff0000 -DSIZE=0x1 -DBITS=32
|
||||
# RUN: not llvm-readobj --unwind %t4 2>&1 \
|
||||
# RUN: | FileCheck %s -DFILE=%t4 --check-prefix=BROKEN-CONTENT -DOFFSET=0xffff0000 -DSIZE=0x1
|
||||
|
||||
# RUN: yaml2obj --docnum=4 %s -o %t5 -DOFFSET=0x1 -DSIZE=0xffff0000 -DBITS=32
|
||||
# RUN: not llvm-readobj --unwind %t5 2>&1 \
|
||||
# RUN: | FileCheck %s -DFILE=%t5 --check-prefix=BROKEN-CONTENT -DOFFSET=0x1 -DSIZE=0xffff0000
|
||||
|
||||
# BROKEN-CONTENT: EHFrameHeader {
|
||||
# BROKEN-CONTENT-NEXT: Address: 0x0
|
||||
# BROKEN-CONTENT-NEXT: Offset: [[OFFSET]]
|
||||
# BROKEN-CONTENT-NEXT: Size: [[SIZE]]
|
||||
# BROKEN-CONTENT-NEXT: Corresponding Section:
|
||||
# BROKEN-CONTENT-NEXT: error: '[[FILE]]': program header [index 0] has a p_offset ([[OFFSET]]) + p_filesz ([[SIZE]]) that is greater than the file size (0xe0)
|
||||
|
||||
--- !ELF
|
||||
FileHeader:
|
||||
Class: ELFCLASS[[BITS]]
|
||||
Data: ELFDATA2LSB
|
||||
Type: ET_EXEC
|
||||
Machine: EM_NONE
|
||||
ProgramHeaders:
|
||||
- Type: PT_GNU_EH_FRAME
|
||||
MemSize: [[SIZE]]
|
||||
FileSize: [[SIZE]]
|
||||
Offset: [[OFFSET]]
|
||||
Sections: []
|
||||
|
||||
## Case B: test we report an error when the file size of the PT_GNU_EH_FRAME
|
||||
## is invalid (goes past the end of the file).
|
||||
# RUN: yaml2obj --docnum=4 %s -o %t6 -DOFFSET=0x100 -DSIZE=0xffff0000 -DBITS=32
|
||||
# RUN: not llvm-readobj --unwind %t6 2>&1 \
|
||||
# RUN: | FileCheck %s -DFILE=%t6 --check-prefix=BROKEN-CONTENT -DOFFSET=0x100 -DSIZE=0xffff0000
|
||||
|
||||
## Case C: test we report an error when the offset + the file size of the PT_GNU_EH_FRAME is so large a
|
||||
## value that it overflows the platform address size type.
|
||||
|
||||
# RUN: yaml2obj --docnum=4 %s -o %t7 -DOFFSET=0x1 -DSIZE=0xffffffff -DBITS=32
|
||||
# RUN: not llvm-readobj --unwind %t7 2>&1 | FileCheck %s -DFILE=%t7 --check-prefix=BROKEN-CONTENT2 -DOFFSET=0x1 -DSIZE=0xffffffff
|
||||
|
||||
# RUN: yaml2obj --docnum=4 %s -o %t8 -DOFFSET=0xffffffff -DSIZE=0x1 -DBITS=32
|
||||
# RUN: not llvm-readobj --unwind %t8 2>&1 | FileCheck %s -DFILE=%t8 --check-prefix=BROKEN-CONTENT2 -DOFFSET=0xffffffff -DSIZE=0x1
|
||||
|
||||
# RUN: yaml2obj --docnum=4 %s -o %t9 -DOFFSET=0x1 -DSIZE=0xffffffffffffffff -DBITS=64
|
||||
# RUN: not llvm-readelf --unwind %t9 2>&1 | FileCheck %s -DFILE=%t9 --check-prefix=BROKEN-CONTENT2 -DOFFSET=0x1 -DSIZE=0xffffffffffffffff
|
||||
|
||||
# RUN: yaml2obj --docnum=4 %s -o %t10 -DOFFSET=0xffffffffffffffff -DSIZE=0x1 -DBITS=64
|
||||
# RUN: not llvm-readelf --unwind %t10 2>&1 | FileCheck %s -DFILE=%t10 --check-prefix=BROKEN-CONTENT2 -DOFFSET=0xffffffffffffffff -DSIZE=0x1
|
||||
|
||||
# BROKEN-CONTENT2: EHFrameHeader {
|
||||
# BROKEN-CONTENT2-NEXT: Address: 0x0
|
||||
# BROKEN-CONTENT2-NEXT: Offset: [[OFFSET]]
|
||||
# BROKEN-CONTENT2-NEXT: Size: [[SIZE]]
|
||||
# BROKEN-CONTENT2-NEXT: Corresponding Section:
|
||||
# BROKEN-CONTENT2-NEXT: error: '[[FILE]]': program header [index 0] has a p_offset ([[OFFSET]]) + p_filesz ([[SIZE]]) that cannot be represented
|
||||
|
@ -33,8 +33,7 @@ class PrinterContext {
|
||||
ScopedPrinter &W;
|
||||
const object::ELFObjectFile<ELFT> *ObjF;
|
||||
|
||||
void printEHFrameHdr(uint64_t Offset, uint64_t Address, uint64_t Size) const;
|
||||
|
||||
void printEHFrameHdr(const typename ELFT::Phdr *EHFramePHdr) const;
|
||||
void printEHFrame(const typename ELFT::Shdr *EHFrameShdr) const;
|
||||
|
||||
public:
|
||||
@ -60,7 +59,6 @@ findSectionByAddress(const object::ELFObjectFile<ELFT> *ObjF, uint64_t Addr) {
|
||||
template <typename ELFT>
|
||||
void PrinterContext<ELFT>::printUnwindInformation() const {
|
||||
const object::ELFFile<ELFT> *Obj = ObjF->getELFFile();
|
||||
const typename ELFT::Phdr *EHFramePhdr = nullptr;
|
||||
|
||||
auto PHs = Obj->program_headers();
|
||||
if (Error E = PHs.takeError())
|
||||
@ -68,19 +66,15 @@ void PrinterContext<ELFT>::printUnwindInformation() const {
|
||||
|
||||
for (const auto &Phdr : *PHs) {
|
||||
if (Phdr.p_type == ELF::PT_GNU_EH_FRAME) {
|
||||
EHFramePhdr = &Phdr;
|
||||
if (Phdr.p_memsz != Phdr.p_filesz)
|
||||
reportError(object::createError(
|
||||
"p_memsz does not match p_filesz for GNU_EH_FRAME"),
|
||||
ObjF->getFileName());
|
||||
printEHFrameHdr(&Phdr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (EHFramePhdr)
|
||||
printEHFrameHdr(EHFramePhdr->p_offset, EHFramePhdr->p_vaddr,
|
||||
EHFramePhdr->p_memsz);
|
||||
|
||||
auto Sections = Obj->sections();
|
||||
if (Error E = Sections.takeError())
|
||||
reportError(std::move(E), ObjF->getFileName());
|
||||
@ -96,16 +90,16 @@ void PrinterContext<ELFT>::printUnwindInformation() const {
|
||||
}
|
||||
|
||||
template <typename ELFT>
|
||||
void PrinterContext<ELFT>::printEHFrameHdr(uint64_t EHFrameHdrOffset,
|
||||
uint64_t EHFrameHdrAddress,
|
||||
uint64_t EHFrameHdrSize) const {
|
||||
void PrinterContext<ELFT>::printEHFrameHdr(const typename ELFT::Phdr *EHFramePHdr) const {
|
||||
DictScope L(W, "EHFrameHeader");
|
||||
uint64_t EHFrameHdrAddress = EHFramePHdr->p_vaddr;
|
||||
W.startLine() << format("Address: 0x%" PRIx64 "\n", EHFrameHdrAddress);
|
||||
W.startLine() << format("Offset: 0x%" PRIx64 "\n", EHFrameHdrOffset);
|
||||
W.startLine() << format("Size: 0x%" PRIx64 "\n", EHFrameHdrSize);
|
||||
W.startLine() << format("Offset: 0x%" PRIx64 "\n", (uint64_t)EHFramePHdr->p_offset);
|
||||
W.startLine() << format("Size: 0x%" PRIx64 "\n", (uint64_t)EHFramePHdr->p_memsz);
|
||||
|
||||
const object::ELFFile<ELFT> *Obj = ObjF->getELFFile();
|
||||
const auto *EHFrameHdrShdr = findSectionByAddress(ObjF, EHFrameHdrAddress);
|
||||
const typename ELFT::Shdr *EHFrameHdrShdr =
|
||||
findSectionByAddress(ObjF, EHFramePHdr->p_vaddr);
|
||||
if (EHFrameHdrShdr) {
|
||||
auto SectionName = Obj->getSectionName(EHFrameHdrShdr);
|
||||
if (Error E = SectionName.takeError())
|
||||
@ -114,7 +108,11 @@ void PrinterContext<ELFT>::printEHFrameHdr(uint64_t EHFrameHdrOffset,
|
||||
W.printString("Corresponding Section", *SectionName);
|
||||
}
|
||||
|
||||
DataExtractor DE(makeArrayRef(Obj->base() + EHFrameHdrOffset, EHFrameHdrSize),
|
||||
Expected<ArrayRef<uint8_t>> Content = Obj->getSegmentContents(EHFramePHdr);
|
||||
if (!Content)
|
||||
reportError(Content.takeError(), ObjF->getFileName());
|
||||
|
||||
DataExtractor DE(*Content,
|
||||
ELFT::TargetEndianness == support::endianness::little,
|
||||
ELFT::Is64Bits ? 8 : 4);
|
||||
|
||||
@ -154,7 +152,7 @@ void PrinterContext<ELFT>::printEHFrameHdr(uint64_t EHFrameHdrOffset,
|
||||
|
||||
unsigned NumEntries = 0;
|
||||
uint64_t PrevPC = 0;
|
||||
while (Offset + 8 <= EHFrameHdrSize && NumEntries < FDECount) {
|
||||
while (Offset + 8 <= EHFramePHdr->p_memsz && NumEntries < FDECount) {
|
||||
DictScope D(W, std::string("entry ") + std::to_string(NumEntries));
|
||||
|
||||
auto InitialPC = DE.getSigned(&Offset, 4) + EHFrameHdrAddress;
|
||||
|
Loading…
x
Reference in New Issue
Block a user