Re: [PATCH] mm: Ensure get_unmapped_area() returns higher addressthanmmap_min_addr

From: Naoya Horiguchi
Date: Wed Oct 23 2013 - 00:26:17 EST


Hi,

On Wed, Oct 23, 2013 at 11:46:53AM +0900, Akira Takeuchi wrote:
> This patch fixes the problem that get_unmapped_area() can return illegal
> address and result in failing mmap(2) etc.
>
> In case that the address higher than PAGE_SIZE is set to
> /proc/sys/vm/mmap_min_addr, the address lower than mmap_min_addr can be
> returned by get_unmapped_area(), even if you do not pass any virtual
> address hint (i.e. the second argument).
>
> This is because the current get_unmapped_area() code does not take into
> account mmap_min_addr.
>
> This leads to two actual problems as follows:
>
> 1. mmap(2) can fail with EPERM on the process without CAP_SYS_RAWIO,
> although any illegal parameter is not passed.
>
> 2. The bottom-up search path after the top-down search might not work in
> arch_get_unmapped_area_topdown().
>
> [How to reproduce]
>
> --- test.c -------------------------------------------------
> #include <stdio.h>
> #include <unistd.h>
> #include <sys/mman.h>
> #include <sys/errno.h>
>
> int main(int argc, char *argv[])
> {
> void *ret = NULL, *last_map;
> size_t pagesize = sysconf(_SC_PAGESIZE);
>
> do {
> last_map = ret;
> ret = mmap(0, pagesize, PROT_NONE,
> MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
> // printf("ret=%p\n", ret);
> } while (ret != MAP_FAILED);
>
> if (errno != ENOMEM) {
> printf("ERR: unexpected errno: %d (last map=%p)\n",
> errno, last_map);
> }
>
> return 0;
> }
> ---------------------------------------------------------------
>
> $ gcc -m32 -o test test.c
> $ sudo sysctl -w vm.mmap_min_addr=65536
> vm.mmap_min_addr = 65536
> $ ./test (run as non-priviledge user)
> ERR: unexpected errno: 1 (last map=0x10000)
>
> Signed-off-by: Akira Takeuchi <takeuchi.akr@xxxxxxxxxxxxxxxx>
> Signed-off-by: Kiyoshi Owada <owada.kiyoshi@xxxxxxxxxxxxxxxx>
> ---
> mm/mmap.c | 10 +++++-----
> 1 files changed, 5 insertions(+), 5 deletions(-)
>
> diff --git a/mm/mmap.c b/mm/mmap.c
> index 9d54851..362e5f1 100644
> --- a/mm/mmap.c
> +++ b/mm/mmap.c
> @@ -1856,7 +1856,7 @@ arch_get_unmapped_area(struct file *filp, unsigned long addr,
> struct vm_area_struct *vma;
> struct vm_unmapped_area_info info;
>
> - if (len > TASK_SIZE)
> + if (len > TASK_SIZE - mmap_min_addr)
> return -ENOMEM;
>
> if (flags & MAP_FIXED)

I feel that it looks clearer to fix this in round_hint_to_min(),
with doing mmap_min_addr check in hint == NULL case.
Does it work for you?

Thanks,
Naoya Horiguchi

> @@ -1865,7 +1865,7 @@ arch_get_unmapped_area(struct file *filp, unsigned long addr,
> if (addr) {
> addr = PAGE_ALIGN(addr);
> vma = find_vma(mm, addr);
> - if (TASK_SIZE - len >= addr &&
> + if (TASK_SIZE - len >= addr && addr >= mmap_min_addr &&
> (!vma || addr + len <= vma->vm_start))
> return addr;
> }
> @@ -1895,7 +1895,7 @@ arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
> struct vm_unmapped_area_info info;
>
> /* requested length too big for entire address space */
> - if (len > TASK_SIZE)
> + if (len > TASK_SIZE - mmap_min_addr)
> return -ENOMEM;
>
> if (flags & MAP_FIXED)
> @@ -1905,14 +1905,14 @@ arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
> if (addr) {
> addr = PAGE_ALIGN(addr);
> vma = find_vma(mm, addr);
> - if (TASK_SIZE - len >= addr &&
> + if (TASK_SIZE - len >= addr && addr >= mmap_min_addr &&
> (!vma || addr + len <= vma->vm_start))
> return addr;
> }
>
> info.flags = VM_UNMAPPED_AREA_TOPDOWN;
> info.length = len;
> - info.low_limit = PAGE_SIZE;
> + info.low_limit = max(PAGE_SIZE, mmap_min_addr);
> info.high_limit = mm->mmap_base;
> info.align_mask = 0;
> addr = vm_unmapped_area(&info);
> --
> 1.7.0.4
>
>
> --
> Akira Takeuchi <takeuchi.akr@xxxxxxxxxxxxxxxx>
>
> --
> To unsubscribe, send a message with 'unsubscribe linux-mm' in
> the body to majordomo@xxxxxxxxxx For more info on Linux MM,
> see: http://www.linux-mm.org/ .
> Don't email: <a href=mailto:"dont@xxxxxxxxx";> email@xxxxxxxxx </a>
>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/