Index: llvm/include/llvm/Object/ELF.h =================================================================== --- llvm/include/llvm/Object/ELF.h +++ llvm/include/llvm/Object/ELF.h @@ -64,6 +64,17 @@ return "[unknown index]"; } +template +std::string getPhdrIndexForError(const ELFFile *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 @@ template Expected> getSectionContentsAsArray(const Elf_Shdr *Sec) const; Expected> getSectionContents(const Elf_Shdr *Sec) const; + Expected> getSegmentContents(const Elf_Phdr *Phdr) const; }; using ELF32LEFile = ELFFile; @@ -422,6 +434,26 @@ return makeArrayRef(Start, Size / sizeof(T)); } +template +Expected> +ELFFile::getSegmentContents(const Elf_Phdr *Phdr) const { + uintX_t Offset = Phdr->p_offset; + uintX_t Size = Phdr->p_filesz; + + if (std::numeric_limits::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 Expected> ELFFile::getSectionContents(const Elf_Shdr *Sec) const { Index: llvm/test/tools/llvm-readobj/ELF/unwind.test =================================================================== --- llvm/test/tools/llvm-readobj/ELF/unwind.test +++ llvm/test/tools/llvm-readobj/ELF/unwind.test @@ -262,3 +262,81 @@ 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 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 + +## Case D: test we report an error when the memory size differs from the file size for the PT_GNU_EH_FRAME header. +# RUN: yaml2obj --docnum=5 %s -o %t11 +# RUN: not llvm-readobj --unwind %t11 2>&1 | FileCheck %s -DFILE=%t11 --check-prefix=SIZE-MISMATCH + +# SIZE-MISMATCH: error: '[[FILE]]': p_memsz does not match p_filesz for GNU_EH_FRAME + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_NONE +ProgramHeaders: + - Type: PT_GNU_EH_FRAME + MemSize: 0x1 + FileSize: 0x2 Index: llvm/tools/llvm-readobj/DwarfCFIEHPrinter.h =================================================================== --- llvm/tools/llvm-readobj/DwarfCFIEHPrinter.h +++ llvm/tools/llvm-readobj/DwarfCFIEHPrinter.h @@ -33,8 +33,7 @@ ScopedPrinter &W; const object::ELFObjectFile *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 @@ template void PrinterContext::printUnwindInformation() const { const object::ELFFile *Obj = ObjF->getELFFile(); - const typename ELFT::Phdr *EHFramePhdr = nullptr; auto PHs = Obj->program_headers(); if (Error E = PHs.takeError()) @@ -68,19 +66,15 @@ 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 @@ } template -void PrinterContext::printEHFrameHdr(uint64_t EHFrameHdrOffset, - uint64_t EHFrameHdrAddress, - uint64_t EHFrameHdrSize) const { +void PrinterContext::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 *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 @@ W.printString("Corresponding Section", *SectionName); } - DataExtractor DE(makeArrayRef(Obj->base() + EHFrameHdrOffset, EHFrameHdrSize), + Expected> 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 @@ 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;