Kees Cook <keescook@xxxxxxxxxxxx> writes: > On Tue, Jan 23, 2024 at 12:23:27AM +0900, Jan Bujak wrote: >> On 1/22/24 23:54, Pedro Falcato wrote: >> > Hi! >> > >> > Where did you get that linker script? >> > >> > FWIW, I catched this possible issue in review, and this was already >> > discussed (see my email and Eric's reply): >> > https://lore.kernel.org/all/CAKbZUD3E2if8Sncy+M2YKncc_Zh08-86W6U5wR0ZMazShxbHHA@xxxxxxxxxxxxxx/ >> > >> > This was my original testcase >> > (https://github.com/heatd/elf-bug-questionmark), which convinced the >> > loader to map .data over a cleared .bss. Your bug seems similar, but >> > does the inverse: maps .bss over .data. >> > >> >> I wrote the linker script myself from scratch. > > Do you still need this addressed, or have you been able to adjust the > linker script? (I ask to try to assess the priority of needing to fix > this behavior change...) Kees, I haven't had a chance to test this yet but it occurred to me that there is an easy way to handle this. In our in-memory copy of the elf program headers we can just merge the two segments together. I believe the diff below accomplishes that, and should fix issue. Signed-off-by: "Eric W. Biederman" <ebiederm@xxxxxxxxxxxx> diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 5397b552fbeb..01df7dd1f3b4 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -924,6 +926,31 @@ static int load_elf_binary(struct linux_binprm *bprm) elf_ppnt = elf_phdata; for (i = 0; i < elf_ex->e_phnum; i++, elf_ppnt++) switch (elf_ppnt->p_type) { + case PT_LOAD: + { + /* + * Historically linux ignored all but the + * final .bss segment. Now that linux honors + * all .bss segments, a .bss segment that + * logically is not overlapping but is + * overlapping when it's edges are rounded up + * to page size causes programs to fail. + * + * Handle that case by merging .bss segments + * into the segment they follow. + */ + if (((i + 1) >= elf_ex->e_phnum) || + (elf_ppnt[1].p_type != PT_LOAD) || + (elf_ppnt[1].p_filesz != 0)) + continue; + unsigned long end = + elf_ppnt[0].p_vaddr + elf_ppnt[0].p_memsz; + if (elf_ppnt[1].p_vaddr != end) + continue; + elf_ppnt[0].p_memsz += elf_ppnt[1].p_memsz; + elf_ppnt[1].p_type = PT_NULL; + break; + } case PT_GNU_STACK: if (elf_ppnt->p_flags & PF_X) executable_stack = EXSTACK_ENABLE_X;