]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
mm: keep a guard page below a grow-down stack segment
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 13 Aug 2010 00:54:33 +0000 (17:54 -0700)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 13 Aug 2010 20:20:27 +0000 (13:20 -0700)
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>
mm/memory.c

index 348e2066a4f3ffa4c731c9d7c5f92301f74a69fe..92ef198a76706a74d27a770f4e82482ee1476ee2 100644 (file)
@@ -2629,6 +2629,26 @@ out_release:
        return ret;
 }
 
+/*
+ * 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.
@@ -2642,6 +2662,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));