diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-08-12 17:54:33 -0700 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-08-13 13:27:38 -0700 |
commit | ca2f90176ea230afe03deaa4b0aff0d7f60c3685 (patch) | |
tree | d6a1b7ee131c362f0206517672fd595dded05c3e | |
parent | 4fc8a6cf7b5657cacd6e28d6699950dd00eb5bbb (diff) |
mm: keep a guard page below a grow-down stack segment
commit 320b2b8de12698082609ebbc1a17165727f4c893 upstream.
This is a rather minimally invasive patch to solve the problem of the
user stack growing into a memory mapped area below it. Whenever we fill
the first page of the stack segment, expand the segment down by one
page.
Now, admittedly some odd application might _want_ the stack to grow down
into the preceding memory mapping, and so we may at some point need to
make this a process tunable (some people might also want to have more
than a single page of guarding), but let's try the minimal approach
first.
Tested with trivial application that maps a single page just below the
stack, and then starts recursing. Without this, we will get a SIGSEGV
_after_ the stack has smashed the mapping. With this patch, we'll get a
nice SIGBUS just as the stack touches the page just above the mapping.
Requested-by: Keith Packard <keithp@keithp.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | mm/memory.c | 23 |
1 files changed, 23 insertions, 0 deletions
diff --git a/mm/memory.c b/mm/memory.c index cceea05086b..5b0760feda7 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -2751,6 +2751,26 @@ out_release: } /* + * This is like a special single-page "expand_downwards()", + * except we must first make sure that 'address-PAGE_SIZE' + * doesn't hit another vma. + * + * The "find_vma()" will do the right thing even if we wrap + */ +static inline int check_stack_guard_page(struct vm_area_struct *vma, unsigned long address) +{ + address &= PAGE_MASK; + if ((vma->vm_flags & VM_GROWSDOWN) && address == vma->vm_start) { + address -= PAGE_SIZE; + if (find_vma(vma->vm_mm, address) != vma) + return -ENOMEM; + + expand_stack(vma, address); + } + return 0; +} + +/* * We enter with non-exclusive mmap_sem (to exclude vma changes, * but allow concurrent faults), and pte mapped but not yet locked. * We return with mmap_sem still held, but pte unmapped and unlocked. @@ -2763,6 +2783,9 @@ static int do_anonymous_page(struct mm_struct *mm, struct vm_area_struct *vma, spinlock_t *ptl; pte_t entry; + if (check_stack_guard_page(vma, address) < 0) + return VM_FAULT_SIGBUS; + if (!(flags & FAULT_FLAG_WRITE)) { entry = pte_mkspecial(pfn_pte(my_zero_pfn(address), vma->vm_page_prot)); |