* [PATCH] mm: Ensure get_unmapped_area() returns higher address than mmap_min_addr
@ 2013-10-23 2:46 Akira Takeuchi
2013-10-23 4:26 ` [PATCH] mm: Ensure get_unmapped_area() returns higher addressthan mmap_min_addr Naoya Horiguchi
0 siblings, 1 reply; 7+ messages in thread
From: Akira Takeuchi @ 2013-10-23 2:46 UTC (permalink / raw)
To: linux-mm; +Cc: linux-kernel
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@jp.panasonic.com>
Signed-off-by: Kiyoshi Owada <owada.kiyoshi@jp.panasonic.com>
---
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)
@@ -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@jp.panasonic.com>
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH] mm: Ensure get_unmapped_area() returns higher addressthan mmap_min_addr
2013-10-23 2:46 [PATCH] mm: Ensure get_unmapped_area() returns higher address than mmap_min_addr Akira Takeuchi
@ 2013-10-23 4:26 ` Naoya Horiguchi
2013-10-23 5:53 ` Akira Takeuchi
0 siblings, 1 reply; 7+ messages in thread
From: Naoya Horiguchi @ 2013-10-23 4:26 UTC (permalink / raw)
To: Akira Takeuchi; +Cc: linux-mm, linux-kernel
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@jp.panasonic.com>
> Signed-off-by: Kiyoshi Owada <owada.kiyoshi@jp.panasonic.com>
> ---
> 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@jp.panasonic.com>
>
> --
> To unsubscribe, send a message with 'unsubscribe linux-mm' in
> the body to majordomo@kvack.org. For more info on Linux MM,
> see: http://www.linux-mm.org/ .
> Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
>
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] mm: Ensure get_unmapped_area() returns higher addressthan mmap_min_addr
2013-10-23 4:26 ` [PATCH] mm: Ensure get_unmapped_area() returns higher addressthan mmap_min_addr Naoya Horiguchi
@ 2013-10-23 5:53 ` Akira Takeuchi
2013-10-23 6:50 ` [PATCH] mm: Ensure get_unmapped_area() returns higheraddressthan mmap_min_addr Naoya Horiguchi
0 siblings, 1 reply; 7+ messages in thread
From: Akira Takeuchi @ 2013-10-23 5:53 UTC (permalink / raw)
To: Naoya Horiguchi; +Cc: linux-mm, linux-kernel
Hi,
On Wed, 23 Oct 2013 00:26:05 -0400
Naoya Horiguchi <n-horiguchi@ah.jp.nec.com> wrote:
> 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@jp.panasonic.com>
> > Signed-off-by: Kiyoshi Owada <owada.kiyoshi@jp.panasonic.com>
> > ---
> > 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?
The current round_hint_to_min() code checks and adjusts just for
the hint address, not for "len". Also, it returns no error.
Do you mean adding "len" to the argument of round_hint_to_min()
and making round_hint_to_min() return any error ?
Regards,
Akira Takeuchi
> 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@jp.panasonic.com>
> >
> > --
> > To unsubscribe, send a message with 'unsubscribe linux-mm' in
> > the body to majordomo@kvack.org. For more info on Linux MM,
> > see: http://www.linux-mm.org/ .
> > Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
> >
--
Akira Takeuchi <takeuchi.akr@jp.panasonic.com>
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] mm: Ensure get_unmapped_area() returns higheraddressthan mmap_min_addr
2013-10-23 5:53 ` Akira Takeuchi
@ 2013-10-23 6:50 ` Naoya Horiguchi
2013-10-23 7:39 ` Akira Takeuchi
2013-10-23 7:44 ` [PATCH v2] mm: Ensure get_unmapped_area() returns higher address than mmap_min_addr Akira Takeuchi
0 siblings, 2 replies; 7+ messages in thread
From: Naoya Horiguchi @ 2013-10-23 6:50 UTC (permalink / raw)
To: Akira Takeuchi; +Cc: linux-mm, linux-kernel
On Wed, Oct 23, 2013 at 02:53:03PM +0900, Akira Takeuchi wrote:
> Hi,
>
> On Wed, 23 Oct 2013 00:26:05 -0400
> Naoya Horiguchi <n-horiguchi@ah.jp.nec.com> wrote:
>
> > 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@jp.panasonic.com>
> > > Signed-off-by: Kiyoshi Owada <owada.kiyoshi@jp.panasonic.com>
> > > ---
> > > 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?
>
> The current round_hint_to_min() code checks and adjusts just for
> the hint address, not for "len". Also, it returns no error.
>
> Do you mean adding "len" to the argument of round_hint_to_min()
> and making round_hint_to_min() return any error ?
I thought of just removing (hint != NULL) check in round_hint_to_min(),
but in my rethinking I found that that affects other code and needs
more changes, so your approach is simpler. I drop my suggestion.
The above check is to detect too big request, and in your reproducer
len is small (4096), so I'm a bit confused :)
Although it doesn't fix your problem itself, it's correct,
Reviewed-by: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com>
Thanks,
Naoya Horiguchi
>
> Regards,
> Akira Takeuchi
>
> > 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@jp.panasonic.com>
> > >
> > > --
> > > To unsubscribe, send a message with 'unsubscribe linux-mm' in
> > > the body to majordomo@kvack.org. For more info on Linux MM,
> > > see: http://www.linux-mm.org/ .
> > > Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
> > >
>
> --
> Akira Takeuchi <takeuchi.akr@jp.panasonic.com>
>
> --
> To unsubscribe, send a message with 'unsubscribe linux-mm' in
> the body to majordomo@kvack.org. For more info on Linux MM,
> see: http://www.linux-mm.org/ .
> Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
>
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] mm: Ensure get_unmapped_area() returns higheraddressthan mmap_min_addr
2013-10-23 6:50 ` [PATCH] mm: Ensure get_unmapped_area() returns higheraddressthan mmap_min_addr Naoya Horiguchi
@ 2013-10-23 7:39 ` Akira Takeuchi
2013-10-23 7:44 ` [PATCH v2] mm: Ensure get_unmapped_area() returns higher address than mmap_min_addr Akira Takeuchi
1 sibling, 0 replies; 7+ messages in thread
From: Akira Takeuchi @ 2013-10-23 7:39 UTC (permalink / raw)
To: Naoya Horiguchi; +Cc: linux-mm, linux-kernel
On Wed, 23 Oct 2013 02:50:49 -0400
Naoya Horiguchi <n-horiguchi@ah.jp.nec.com> wrote:
> On Wed, Oct 23, 2013 at 02:53:03PM +0900, Akira Takeuchi wrote:
> > Hi,
> >
> > On Wed, 23 Oct 2013 00:26:05 -0400
> > Naoya Horiguchi <n-horiguchi@ah.jp.nec.com> wrote:
> >
> > > 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@jp.panasonic.com>
> > > > Signed-off-by: Kiyoshi Owada <owada.kiyoshi@jp.panasonic.com>
> > > > ---
> > > > 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?
> >
> > The current round_hint_to_min() code checks and adjusts just for
> > the hint address, not for "len". Also, it returns no error.
> >
> > Do you mean adding "len" to the argument of round_hint_to_min()
> > and making round_hint_to_min() return any error ?
>
> I thought of just removing (hint != NULL) check in round_hint_to_min(),
> but in my rethinking I found that that affects other code and needs
> more changes, so your approach is simpler. I drop my suggestion.
>
> The above check is to detect too big request, and in your reproducer
> len is small (4096), so I'm a bit confused :)
> Although it doesn't fix your problem itself, it's correct,
I'll add a description as a "Note" to my v2 patch to avoid such
confusing. The difference between v1 and v2 patch is only adding
the "Note". Thank you for reviewing !
Regadrs,
Akira Takeuchi
> Reviewed-by: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com>
>
> Thanks,
> Naoya Horiguchi
>
> >
> > Regards,
> > Akira Takeuchi
> >
> > > 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@jp.panasonic.com>
> > > >
> > > > --
> > > > To unsubscribe, send a message with 'unsubscribe linux-mm' in
> > > > the body to majordomo@kvack.org. For more info on Linux MM,
> > > > see: http://www.linux-mm.org/ .
> > > > Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
> > > >
> >
> > --
> > Akira Takeuchi <takeuchi.akr@jp.panasonic.com>
> >
> > --
> > To unsubscribe, send a message with 'unsubscribe linux-mm' in
> > the body to majordomo@kvack.org. For more info on Linux MM,
> > see: http://www.linux-mm.org/ .
> > Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
> >
--
Akira Takeuchi <takeuchi.akr@jp.panasonic.com>
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2] mm: Ensure get_unmapped_area() returns higher address than mmap_min_addr
2013-10-23 6:50 ` [PATCH] mm: Ensure get_unmapped_area() returns higheraddressthan mmap_min_addr Naoya Horiguchi
2013-10-23 7:39 ` Akira Takeuchi
@ 2013-10-23 7:44 ` Akira Takeuchi
2013-10-23 18:30 ` [PATCH v2] mm: Ensure get_unmapped_area() returns higher addressthan mmap_min_addr Naoya Horiguchi
1 sibling, 1 reply; 7+ messages in thread
From: Akira Takeuchi @ 2013-10-23 7:44 UTC (permalink / raw)
To: linux-mm; +Cc: linux-kernel
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>
---
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)
@@ -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@jp.panasonic.com>
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v2] mm: Ensure get_unmapped_area() returns higher addressthan mmap_min_addr
2013-10-23 7:44 ` [PATCH v2] mm: Ensure get_unmapped_area() returns higher address than mmap_min_addr Akira Takeuchi
@ 2013-10-23 18:30 ` Naoya Horiguchi
0 siblings, 0 replies; 7+ messages in thread
From: Naoya Horiguchi @ 2013-10-23 18:30 UTC (permalink / raw)
To: Akira Takeuchi; +Cc: linux-mm, linux-kernel, Andrew Morton
Cc:ed Andrew,
On Wed, Oct 23, 2013 at 04:44:47PM +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().
>
> 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>
BTW, this bug seems to be very old, so I think this patch is worth
going into all of currently living stable trees.
Thanks,
Naoya Horiguchi
> ---
> 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)
> @@ -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@jp.panasonic.com>
>
> --
> To unsubscribe, send a message with 'unsubscribe linux-mm' in
> the body to majordomo@kvack.org. For more info on Linux MM,
> see: http://www.linux-mm.org/ .
> Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
>
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2013-10-23 18:30 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-10-23 2:46 [PATCH] mm: Ensure get_unmapped_area() returns higher address than mmap_min_addr Akira Takeuchi
2013-10-23 4:26 ` [PATCH] mm: Ensure get_unmapped_area() returns higher addressthan mmap_min_addr Naoya Horiguchi
2013-10-23 5:53 ` Akira Takeuchi
2013-10-23 6:50 ` [PATCH] mm: Ensure get_unmapped_area() returns higheraddressthan mmap_min_addr Naoya Horiguchi
2013-10-23 7:39 ` Akira Takeuchi
2013-10-23 7:44 ` [PATCH v2] mm: Ensure get_unmapped_area() returns higher address than mmap_min_addr Akira Takeuchi
2013-10-23 18:30 ` [PATCH v2] mm: Ensure get_unmapped_area() returns higher addressthan mmap_min_addr Naoya Horiguchi
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).