Messages in this thread Patch in this message | | | Date | Tue, 25 Jul 2000 09:40:11 +0200 (MET DST) | From | "Maciej W. Rozycki" <> | Subject | [patch] 2.4.0-test4: mmap() limitations |
| |
Hi,
I am observing the following problem: when a program requests a mmap() and provides a suggested address, Linux only checks addresses above it if the given address does not fit. As a result, the higher address a program specifies, the smaller chance mmap() succeeds even if there is still space available below. If a program specifies an addres above TASK_SIZE, mmap() will never succeed.
Failures due to the described limitation may be observed with the following program (using a kernel compiled for an i386 with the CONFIG_NOHIGHMEM option set; depending on the TASK_SIZE definition this may also be observed for other platforms):
#define _GNU_SOURCE
#include <stdio.h> #include <sys/mman.h>
int main(void) { void *a0, *a1, *a2;
a0 = mmap((void *)0xbfffc000, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); a1 = mmap((void *)0xbfffd000, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); a2 = mmap((void *)0xbfffc000, 0x4000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
fprintf(stderr, "a0: %p, a1: %p, a2: %p\n", a0, a1, a2);
if (a0 != (void *)-1) munmap(a0, 0x1000); if (a1 != (void *)-1) munmap(a1, 0x1000); if (a2 != (void *)-1) munmap(a2, 0x2000);
return 0; }
This limitation makes glibc's runtime loading routines fail from time to time as they always provide a suggested address and the address is computed in run time. Depending on the addresses in an ELF header and other objects currently mmap()ed this may succeed or fail. This is also the reason of a failure of dlopen()ing ld.so (which often happens when calling dlopen() from a statically linked executable) on MIPS/Linux.
The following patch removes this limitation, allowing checking of addresses below the suggested one in the second pass.
This might not be serious enough issue for 2.4.0, but if deferred it should go into 2.4.1 or so rather than into 2.5.
Maciej
-- + Maciej W. Rozycki, Technical University of Gdansk, Poland + +--------------------------------------------------------------+ + e-mail: macro@ds2.pg.gda.pl, PGP key available +
diff -u --recursive --new-file linux-2.4.0-test4.macro/mm/mmap.c linux-2.4.0-test4/mm/mmap.c --- linux-2.4.0-test4.macro/mm/mmap.c Sun Jul 16 22:27:29 2000 +++ linux-2.4.0-test4/mm/mmap.c Tue Jul 25 05:06:21 2000 @@ -175,7 +175,7 @@ if ((len = PAGE_ALIGN(len)) == 0) return addr; - if (len > TASK_SIZE || addr > TASK_SIZE-len) + if (len > TASK_SIZE || ((flags & MAP_FIXED) && (addr > TASK_SIZE - len))) return -EINVAL; /* offset overflow? */ @@ -356,20 +356,31 @@ unsigned long get_unmapped_area(unsigned long addr, unsigned long len) { struct vm_area_struct * vmm; + int pass = 0; if (len > TASK_SIZE) return 0; - if (!addr) - addr = TASK_UNMAPPED_BASE; - addr = PAGE_ALIGN(addr); - - for (vmm = find_vma(current->mm, addr); ; vmm = vmm->vm_next) { - /* At this point: (!vmm || addr < vmm->vm_end). */ - if (TASK_SIZE - len < addr) - return 0; - if (!vmm || addr + len <= vmm->vm_start) - return addr; - addr = vmm->vm_end; + + while (1) { + if (!addr) + addr = TASK_UNMAPPED_BASE; + addr = PAGE_ALIGN(addr); + + for (vmm = find_vma(current->mm, addr); ; vmm = vmm->vm_next) { + /* At this point: (!vmm || addr < vmm->vm_end). */ + if (TASK_SIZE - len < addr) { + if (pass > 0) + return 0; + else { + pass = 1; + addr = 0; + break; + } + } + if (!vmm || addr + len <= vmm->vm_start) + return addr; + addr = vmm->vm_end; + } } } #endif
- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu Please read the FAQ at http://www.tux.org/lkml/
| |