From 68a5a23abcb9649f05728db4cf50bb4498937855 Mon Sep 17 00:00:00 2001 From: Rich Felker Date: Tue, 26 Jun 2018 12:15:13 -0400 Subject: fix dynamic linker mapping/clearing bss in first/only LOAD segment writable load segments can have size-in-memory larger than their size in the ELF file, representing bss or equivalent. the initial partial page has to be zero-filled, and additional anonymous pages have to be mapped such that accesses don't failt with SIGBUS. map_library skips redundant MAP_FIXED mapping of the initial (lowest-address) segment when processing LOAD segments since it was already mapped when reserving the virtual address range, but in doing so, inadvertently also skipped the code to fill/map bss. typical executable and library files have two or more LOAD segments, and the first one is text/rodata (non-writable) and thus has no bss, but it is syntactically valid for an ELF program/library to put its writable segment first, or to have only one segment (everything writable). the binutils bfd-based linker has been observed to create such programs in the presence of unusual sections or linker scripts. fix by moving only the mmap_fixed operation under the conditional rather than skipping the remainder of the loop body. add a check to avoid bss processing in the case where the segment is not writable; this should not happen, but if it does, the change would be a crashing regression without this check. --- ldso/dynlink.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'ldso') diff --git a/ldso/dynlink.c b/ldso/dynlink.c index c6216b7c..eecbddb5 100644 --- a/ldso/dynlink.c +++ b/ldso/dynlink.c @@ -706,18 +706,17 @@ static void *map_library(int fd, struct dso *dso) dso->phnum = eh->e_phnum; dso->phentsize = eh->e_phentsize; } - /* Reuse the existing mapping for the lowest-address LOAD */ - if ((ph->p_vaddr & -PAGE_SIZE) == addr_min && !DL_NOMMU_SUPPORT) - continue; this_min = ph->p_vaddr & -PAGE_SIZE; this_max = ph->p_vaddr+ph->p_memsz+PAGE_SIZE-1 & -PAGE_SIZE; off_start = ph->p_offset & -PAGE_SIZE; prot = (((ph->p_flags&PF_R) ? PROT_READ : 0) | ((ph->p_flags&PF_W) ? PROT_WRITE: 0) | ((ph->p_flags&PF_X) ? PROT_EXEC : 0)); - if (mmap_fixed(base+this_min, this_max-this_min, prot, MAP_PRIVATE|MAP_FIXED, fd, off_start) == MAP_FAILED) - goto error; - if (ph->p_memsz > ph->p_filesz) { + /* Reuse the existing mapping for the lowest-address LOAD */ + if ((ph->p_vaddr & -PAGE_SIZE) != addr_min || DL_NOMMU_SUPPORT) + if (mmap_fixed(base+this_min, this_max-this_min, prot, MAP_PRIVATE|MAP_FIXED, fd, off_start) == MAP_FAILED) + goto error; + if (ph->p_memsz > ph->p_filesz && (ph->p_flags&PF_W)) { size_t brk = (size_t)base+ph->p_vaddr+ph->p_filesz; size_t pgbrk = brk+PAGE_SIZE-1 & -PAGE_SIZE; memset((void *)brk, 0, pgbrk-brk & PAGE_SIZE-1); -- cgit v1.2.1