aboutsummaryrefslogtreecommitdiffstats
path: root/mm
diff options
context:
space:
mode:
authorAkira Takeuchi <takeuchi.akr@jp.panasonic.com>2013-11-12 18:08:21 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-12-04 13:56:39 -0500
commit3cbafaa72d3afc4a4177f0d76178cd26a525589b (patch)
tree68c0918c8d37ccb9e19c1ecc1660bab4939192e5 /mm
parentdf3e475e43ae5e2112a464335c28368092d53edd (diff)
mm: ensure get_unmapped_area() returns higher address than mmap_min_addr
commit 2afc745f3e3079ab16c826be4860da2529054dd2 upstream. 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(). Note: The first and third chunk of my patch, which changes "len" check, are for more precise check using mmap_min_addr, and not for solving the above problem. [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@jp.panasonic.com> Signed-off-by: Kiyoshi Owada <owada.kiyoshi@jp.panasonic.com> Reviewed-by: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'mm')
-rw-r--r--mm/mmap.c10
1 files changed, 5 insertions, 5 deletions
diff --git a/mm/mmap.c b/mm/mmap.c
index 8d25fdc653be..8f87b14c7968 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -1853,7 +1853,7 @@ arch_get_unmapped_area(struct file *filp, unsigned long addr,
1853 struct vm_area_struct *vma; 1853 struct vm_area_struct *vma;
1854 struct vm_unmapped_area_info info; 1854 struct vm_unmapped_area_info info;
1855 1855
1856 if (len > TASK_SIZE) 1856 if (len > TASK_SIZE - mmap_min_addr)
1857 return -ENOMEM; 1857 return -ENOMEM;
1858 1858
1859 if (flags & MAP_FIXED) 1859 if (flags & MAP_FIXED)
@@ -1862,7 +1862,7 @@ arch_get_unmapped_area(struct file *filp, unsigned long addr,
1862 if (addr) { 1862 if (addr) {
1863 addr = PAGE_ALIGN(addr); 1863 addr = PAGE_ALIGN(addr);
1864 vma = find_vma(mm, addr); 1864 vma = find_vma(mm, addr);
1865 if (TASK_SIZE - len >= addr && 1865 if (TASK_SIZE - len >= addr && addr >= mmap_min_addr &&
1866 (!vma || addr + len <= vma->vm_start)) 1866 (!vma || addr + len <= vma->vm_start))
1867 return addr; 1867 return addr;
1868 } 1868 }
@@ -1901,7 +1901,7 @@ arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
1901 struct vm_unmapped_area_info info; 1901 struct vm_unmapped_area_info info;
1902 1902
1903 /* requested length too big for entire address space */ 1903 /* requested length too big for entire address space */
1904 if (len > TASK_SIZE) 1904 if (len > TASK_SIZE - mmap_min_addr)
1905 return -ENOMEM; 1905 return -ENOMEM;
1906 1906
1907 if (flags & MAP_FIXED) 1907 if (flags & MAP_FIXED)
@@ -1911,14 +1911,14 @@ arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
1911 if (addr) { 1911 if (addr) {
1912 addr = PAGE_ALIGN(addr); 1912 addr = PAGE_ALIGN(addr);
1913 vma = find_vma(mm, addr); 1913 vma = find_vma(mm, addr);
1914 if (TASK_SIZE - len >= addr && 1914 if (TASK_SIZE - len >= addr && addr >= mmap_min_addr &&
1915 (!vma || addr + len <= vma->vm_start)) 1915 (!vma || addr + len <= vma->vm_start))
1916 return addr; 1916 return addr;
1917 } 1917 }
1918 1918
1919 info.flags = VM_UNMAPPED_AREA_TOPDOWN; 1919 info.flags = VM_UNMAPPED_AREA_TOPDOWN;
1920 info.length = len; 1920 info.length = len;
1921 info.low_limit = PAGE_SIZE; 1921 info.low_limit = max(PAGE_SIZE, mmap_min_addr);
1922 info.high_limit = mm->mmap_base; 1922 info.high_limit = mm->mmap_base;
1923 info.align_mask = 0; 1923 info.align_mask = 0;
1924 addr = vm_unmapped_area(&info); 1924 addr = vm_unmapped_area(&info);