[PATCH] D43819: [ELF] - Restrict section offsets that exceeds file size.
Rafael Ávila de Espíndola via Phabricator via llvm-commits
llvm-commits at lists.llvm.org
Mon Mar 12 09:15:20 PDT 2018
rafael added a comment.
LGTM.
I think the comment can probably be simplified, but that can be a
followup.
Cheers,
Rafael
George Rimar via Phabricator <reviews at reviews.llvm.org> writes:
> grimar updated this revision to Diff 137976.
> grimar added a comment.
>
> - Addressed review comment.
>
> https://reviews.llvm.org/D43819
>
> Files:
>
> ELF/Writer.cpp
> test/ELF/linkerscript/Inputs/sections-va-overflow.s
> test/ELF/linkerscript/sections-va-overflow.test
>
> Index: test/ELF/linkerscript/sections-va-overflow.test
> ===================================================================
>
> - test/ELF/linkerscript/sections-va-overflow.test +++ test/ELF/linkerscript/sections-va-overflow.test @@ -0,0 +1,22 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/sections-va-overflow.s -o %t.o +# RUN: not ld.lld -o /dev/null --script %s %t.o 2>&1 | FileCheck %s -check-prefix=ERR + +PHDRS { + ph_headers PT_PHDR PHDRS; + ph_text PT_LOAD FILEHDR PHDRS FLAGS (0x1 | 0x4); +} + +SECTIONS { + . = 0xffffffff20000000; + .text : { *(.text*) } : ph_text + .test 0x1000 : { BYTE(0) } + .bss : { *(.bss*) } +} + +## Section .test has VA 0x1000 and placed in the same segment as section .text +## with VA 0xffffffff20000000. That might be technically correct, but most probably +## is a result of a broken script file and causes file offset calculation overflow. +## It seems we do not have to support it, so we don't and we report an error in this case. +# ERR: error: unable to place section .text at file offset [0xFFFFFFFF20000000, 0xFFFFFFFE40000000]; check your linker script for overflows +# ERR-NOT: unable to place section .bss Index: test/ELF/linkerscript/Inputs/sections-va-overflow.s ===================================================================
> - test/ELF/linkerscript/Inputs/sections-va-overflow.s +++ test/ELF/linkerscript/Inputs/sections-va-overflow.s @@ -0,0 +1,6 @@ +.global _start +_start: + retq + +.bss +.space 0x2000 Index: ELF/Writer.cpp ===================================================================
> - ELF/Writer.cpp +++ ELF/Writer.cpp @@ -1873,6 +1873,12 @@ FileSize = alignTo(Off, Config->Wordsize); }
>
> +static std::string rangeToString(uint64_t Addr, uint64_t Len) { + if (Len == 0) + return "<empty range at 0x" + utohexstr(Addr) + ">"; + return "[0x" + utohexstr(Addr) + ", 0x" + utohexstr(Addr + Len - 1) + "]"; +} + // Assign file offsets to output sections. template <class ELFT> void Writer<ELFT>::assignFileOffsets() { uint64_t Off = 0; @@ -1897,6 +1903,25 @@
>
> SectionHeaderOff = alignTo(Off, Config->Wordsize); FileSize = SectionHeaderOff + (OutputSections.size() + 1) * sizeof(Elf_Shdr); + + // Our logic assumes that sections have rising VA within the same segment. + // With use of linker scripts it is possible to violate this rule and get file + // offset overlaps or overflows. That should never happen with a valid script + // which does not move the location counter backwards and usually scripts do + // not do that. Unfortunately, there are apps in the wild, for example, Linux + // kernel, which control segment distribution explicitly and move the counter + // backwards, so we have to allow doing that to support linking them. We + // perform non-critical checks for overlaps in checkNoOverlappingSections(), + // but here we want to prevent file size overflows because it would crash the + // linker. + for (OutputSection *Sec : OutputSections) { + if (Sec->Type == SHT_NOBITS) + continue; + if ((Sec->Offset > FileSize) || (Sec->Offset + Sec->Size > FileSize)) + error("unable to place section " + Sec->Name + " at file offset " + + rangeToString(Sec->Offset, Sec->Offset + Sec->Size) + + "; check your linker script for overflows"); + } }
>
> // Finalize the program headers. We call this function after we assign @@ -1935,12 +1960,6 @@ } }
>
> -static std::string rangeToString(uint64_t Addr, uint64_t Len) {
> - if (Len == 0)
> - return "<empty range at 0x" + utohexstr(Addr) + ">";
> - return "[0x" + utohexstr(Addr) + ", 0x" + utohexstr(Addr + Len - 1) + "]"; -} - // Check whether sections overlap for a specific address range (file offsets, // load and virtual adresses). //
>
> Index: test/ELF/linkerscript/sections-va-overflow.test
> ===================================================================
>
> - test/ELF/linkerscript/sections-va-overflow.test +++ test/ELF/linkerscript/sections-va-overflow.test @@ -0,0 +1,22 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/sections-va-overflow.s -o %t.o +# RUN: not ld.lld -o /dev/null --script %s %t.o 2>&1 | FileCheck %s -check-prefix=ERR + +PHDRS { + ph_headers PT_PHDR PHDRS; + ph_text PT_LOAD FILEHDR PHDRS FLAGS (0x1 | 0x4); +} + +SECTIONS { + . = 0xffffffff20000000; + .text : { *(.text*) } : ph_text + .test 0x1000 : { BYTE(0) } + .bss : { *(.bss*) } +} + +## Section .test has VA 0x1000 and placed in the same segment as section .text +## with VA 0xffffffff20000000. That might be technically correct, but most probably +## is a result of a broken script file and causes file offset calculation overflow. +## It seems we do not have to support it, so we don't and we report an error in this case. +# ERR: error: unable to place section .text at file offset [0xFFFFFFFF20000000, 0xFFFFFFFE40000000]; check your linker script for overflows +# ERR-NOT: unable to place section .bss Index: test/ELF/linkerscript/Inputs/sections-va-overflow.s ===================================================================
> - test/ELF/linkerscript/Inputs/sections-va-overflow.s +++ test/ELF/linkerscript/Inputs/sections-va-overflow.s @@ -0,0 +1,6 @@ +.global _start +_start: + retq + +.bss +.space 0x2000 Index: ELF/Writer.cpp ===================================================================
> - ELF/Writer.cpp +++ ELF/Writer.cpp @@ -1873,6 +1873,12 @@ FileSize = alignTo(Off, Config->Wordsize); }
>
> +static std::string rangeToString(uint64_t Addr, uint64_t Len) { + if (Len == 0) + return "<empty range at 0x" + utohexstr(Addr) + ">"; + return "[0x" + utohexstr(Addr) + ", 0x" + utohexstr(Addr + Len - 1) + "]"; +} + // Assign file offsets to output sections. template <class ELFT> void Writer<ELFT>::assignFileOffsets() { uint64_t Off = 0; @@ -1897,6 +1903,25 @@
>
> SectionHeaderOff = alignTo(Off, Config->Wordsize); FileSize = SectionHeaderOff + (OutputSections.size() + 1) * sizeof(Elf_Shdr); + + // Our logic assumes that sections have rising VA within the same segment. + // With use of linker scripts it is possible to violate this rule and get file + // offset overlaps or overflows. That should never happen with a valid script + // which does not move the location counter backwards and usually scripts do + // not do that. Unfortunately, there are apps in the wild, for example, Linux + // kernel, which control segment distribution explicitly and move the counter + // backwards, so we have to allow doing that to support linking them. We + // perform non-critical checks for overlaps in checkNoOverlappingSections(), + // but here we want to prevent file size overflows because it would crash the + // linker. + for (OutputSection *Sec : OutputSections) { + if (Sec->Type == SHT_NOBITS) + continue; + if ((Sec->Offset > FileSize) || (Sec->Offset + Sec->Size > FileSize)) + error("unable to place section " + Sec->Name + " at file offset " + + rangeToString(Sec->Offset, Sec->Offset + Sec->Size) + + "; check your linker script for overflows"); + } }
>
> // Finalize the program headers. We call this function after we assign @@ -1935,12 +1960,6 @@ } }
>
> -static std::string rangeToString(uint64_t Addr, uint64_t Len) {
> - if (Len == 0)
> - return "<empty range at 0x" + utohexstr(Addr) + ">";
> - return "[0x" + utohexstr(Addr) + ", 0x" + utohexstr(Addr + Len - 1) + "]"; -} - // Check whether sections overlap for a specific address range (file offsets, // load and virtual adresses). //
https://reviews.llvm.org/D43819
More information about the llvm-commits
mailing list