aboutsummaryrefslogtreecommitdiffstats
path: root/mm/mmap.c
diff options
context:
space:
mode:
authorCyril Hrubis <chrubis@suse.cz>2013-04-29 18:08:33 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2013-04-29 18:54:38 -0400
commite8420a8ece80b3fe810415ecf061d54ca7fab266 (patch)
tree0da34b1c39cec5bcf6045ac6ea51155b3692553d /mm/mmap.c
parent70ddf637eebe47e61fb2be08a59315581b6d2f38 (diff)
mm/mmap: check for RLIMIT_AS before unmapping
Fix a corner case for MAP_FIXED when requested mapping length is larger than rlimit for virtual memory. In such case any overlapping mappings are unmapped before we check for the limit and return ENOMEM. The check is moved before the loop that unmaps overlapping parts of existing mappings. When we are about to hit the limit (currently mapped pages + len > limit) we scan for overlapping pages and check again accounting for them. This fixes situation when userspace program expects that the previous mappings are preserved after the mmap() syscall has returned with error. (POSIX clearly states that successfull mapping shall replace any previous mappings.) This corner case was found and can be tested with LTP testcase: testcases/open_posix_testsuite/conformance/interfaces/mmap/24-2.c In this case the mmap, which is clearly over current limit, unmaps dynamic libraries and the testcase segfaults right after returning into userspace. I've also looked at the second instance of the unmapping loop in the do_brk(). The do_brk() is called from brk() syscall and from vm_brk(). The brk() syscall checks for overlapping mappings and bails out when there are any (so it can't be triggered from the brk syscall). The vm_brk() is called only from binmft handlers so it shouldn't be triggered unless binmft handler created overlapping mappings. Signed-off-by: Cyril Hrubis <chrubis@suse.cz> Reviewed-by: Mel Gorman <mgorman@suse.de> Reviewed-by: Wanpeng Li <liwanp@linux.vnet.ibm.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/mmap.c')
-rw-r--r--mm/mmap.c50
1 files changed, 46 insertions, 4 deletions
diff --git a/mm/mmap.c b/mm/mmap.c
index 43c4955535aa..da3e9c04bf37 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -6,6 +6,7 @@
6 * Address space accounting code <alan@lxorguk.ukuu.org.uk> 6 * Address space accounting code <alan@lxorguk.ukuu.org.uk>
7 */ 7 */
8 8
9#include <linux/kernel.h>
9#include <linux/slab.h> 10#include <linux/slab.h>
10#include <linux/backing-dev.h> 11#include <linux/backing-dev.h>
11#include <linux/mm.h> 12#include <linux/mm.h>
@@ -550,6 +551,34 @@ static int find_vma_links(struct mm_struct *mm, unsigned long addr,
550 return 0; 551 return 0;
551} 552}
552 553
554static unsigned long count_vma_pages_range(struct mm_struct *mm,
555 unsigned long addr, unsigned long end)
556{
557 unsigned long nr_pages = 0;
558 struct vm_area_struct *vma;
559
560 /* Find first overlaping mapping */
561 vma = find_vma_intersection(mm, addr, end);
562 if (!vma)
563 return 0;
564
565 nr_pages = (min(end, vma->vm_end) -
566 max(addr, vma->vm_start)) >> PAGE_SHIFT;
567
568 /* Iterate over the rest of the overlaps */
569 for (vma = vma->vm_next; vma; vma = vma->vm_next) {
570 unsigned long overlap_len;
571
572 if (vma->vm_start > end)
573 break;
574
575 overlap_len = min(end, vma->vm_end) - vma->vm_start;
576 nr_pages += overlap_len >> PAGE_SHIFT;
577 }
578
579 return nr_pages;
580}
581
553void __vma_link_rb(struct mm_struct *mm, struct vm_area_struct *vma, 582void __vma_link_rb(struct mm_struct *mm, struct vm_area_struct *vma,
554 struct rb_node **rb_link, struct rb_node *rb_parent) 583 struct rb_node **rb_link, struct rb_node *rb_parent)
555{ 584{
@@ -1442,6 +1471,23 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
1442 unsigned long charged = 0; 1471 unsigned long charged = 0;
1443 struct inode *inode = file ? file_inode(file) : NULL; 1472 struct inode *inode = file ? file_inode(file) : NULL;
1444 1473
1474 /* Check against address space limit. */
1475 if (!may_expand_vm(mm, len >> PAGE_SHIFT)) {
1476 unsigned long nr_pages;
1477
1478 /*
1479 * MAP_FIXED may remove pages of mappings that intersects with
1480 * requested mapping. Account for the pages it would unmap.
1481 */
1482 if (!(vm_flags & MAP_FIXED))
1483 return -ENOMEM;
1484
1485 nr_pages = count_vma_pages_range(mm, addr, addr + len);
1486
1487 if (!may_expand_vm(mm, (len >> PAGE_SHIFT) - nr_pages))
1488 return -ENOMEM;
1489 }
1490
1445 /* Clear old maps */ 1491 /* Clear old maps */
1446 error = -ENOMEM; 1492 error = -ENOMEM;
1447munmap_back: 1493munmap_back:
@@ -1451,10 +1497,6 @@ munmap_back:
1451 goto munmap_back; 1497 goto munmap_back;
1452 } 1498 }
1453 1499
1454 /* Check against address space limit. */
1455 if (!may_expand_vm(mm, len >> PAGE_SHIFT))
1456 return -ENOMEM;
1457
1458 /* 1500 /*
1459 * Private writable mapping: check memory availability 1501 * Private writable mapping: check memory availability
1460 */ 1502 */