summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorakpm@osdl.org <akpm@osdl.org>2005-05-01 19:58:35 +0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-05-01 19:58:35 +0400
commit119f657c72fc07d6fd28c61de59cfba1566970a9 (patch)
tree33c8070aa904c4919cf244cdcd7c01e54589bb9e
parentf021e9210185b46e41ec3a0e78ec1621e168eacb (diff)
downloadlinux-119f657c72fc07d6fd28c61de59cfba1566970a9.tar.xz
[PATCH] RLIMIT_AS checking fix
Address bug #4508: there's potential for wraparound in the various places where we perform RLIMIT_AS checking. (I'm a bit worried about acct_stack_growth(). Are we sure that vma->vm_mm is always equal to current->mm? If not, then we're comparing some other process's total_vm with the calling process's rlimits). Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--include/linux/mm.h1
-rw-r--r--mm/mmap.c24
-rw-r--r--mm/mremap.c6
3 files changed, 23 insertions, 8 deletions
diff --git a/include/linux/mm.h b/include/linux/mm.h
index c74a74ca401d..8b007ad2d450 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -726,6 +726,7 @@ extern void __vma_link_rb(struct mm_struct *, struct vm_area_struct *,
extern struct vm_area_struct *copy_vma(struct vm_area_struct **,
unsigned long addr, unsigned long len, pgoff_t pgoff);
extern void exit_mmap(struct mm_struct *);
+extern int may_expand_vm(struct mm_struct *mm, unsigned long npages);
extern unsigned long get_unmapped_area(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
diff --git a/mm/mmap.c b/mm/mmap.c
index 6ea204cc751e..1ec0f6e9c0d8 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -1009,8 +1009,7 @@ munmap_back:
}
/* Check against address space limit. */
- if ((mm->total_vm << PAGE_SHIFT) + len
- > current->signal->rlim[RLIMIT_AS].rlim_cur)
+ if (!may_expand_vm(mm, len >> PAGE_SHIFT))
return -ENOMEM;
if (accountable && (!(flags & MAP_NORESERVE) ||
@@ -1421,7 +1420,7 @@ static int acct_stack_growth(struct vm_area_struct * vma, unsigned long size, un
struct rlimit *rlim = current->signal->rlim;
/* address space limit tests */
- if (mm->total_vm + grow > rlim[RLIMIT_AS].rlim_cur >> PAGE_SHIFT)
+ if (!may_expand_vm(mm, grow))
return -ENOMEM;
/* Stack limit test */
@@ -1848,8 +1847,7 @@ unsigned long do_brk(unsigned long addr, unsigned long len)
}
/* Check against address space limits *after* clearing old maps... */
- if ((mm->total_vm << PAGE_SHIFT) + len
- > current->signal->rlim[RLIMIT_AS].rlim_cur)
+ if (!may_expand_vm(mm, len >> PAGE_SHIFT))
return -ENOMEM;
if (mm->map_count > sysctl_max_map_count)
@@ -2019,3 +2017,19 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap,
}
return new_vma;
}
+
+/*
+ * Return true if the calling process may expand its vm space by the passed
+ * number of pages
+ */
+int may_expand_vm(struct mm_struct *mm, unsigned long npages)
+{
+ unsigned long cur = mm->total_vm; /* pages */
+ unsigned long lim;
+
+ lim = current->signal->rlim[RLIMIT_AS].rlim_cur >> PAGE_SHIFT;
+
+ if (cur + npages > lim)
+ return 0;
+ return 1;
+}
diff --git a/mm/mremap.c b/mm/mremap.c
index 0d1c1b9c7a0a..0dd7ace94e51 100644
--- a/mm/mremap.c
+++ b/mm/mremap.c
@@ -347,10 +347,10 @@ unsigned long do_mremap(unsigned long addr,
if (locked > lock_limit && !capable(CAP_IPC_LOCK))
goto out;
}
- ret = -ENOMEM;
- if ((current->mm->total_vm << PAGE_SHIFT) + (new_len - old_len)
- > current->signal->rlim[RLIMIT_AS].rlim_cur)
+ if (!may_expand_vm(current->mm, (new_len - old_len) >> PAGE_SHIFT)) {
+ ret = -ENOMEM;
goto out;
+ }
if (vma->vm_flags & VM_ACCOUNT) {
charged = (new_len - old_len) >> PAGE_SHIFT;