]> git.ipfire.org Git - people/teissler/ipfire-2.x.git/blobdiff - src/patches/suse-2.6.27.25/patches.suse/silent-stack-overflow
Revert "Move xen patchset to new version's subdir."
[people/teissler/ipfire-2.x.git] / src / patches / suse-2.6.27.25 / patches.suse / silent-stack-overflow
diff --git a/src/patches/suse-2.6.27.25/patches.suse/silent-stack-overflow b/src/patches/suse-2.6.27.25/patches.suse/silent-stack-overflow
new file mode 100644 (file)
index 0000000..fec8030
--- /dev/null
@@ -0,0 +1,466 @@
+From: Andrea Arcangeli <andrea@suse.de>
+Subject: avoid silent stack overflow over the heap
+Patch-mainline: no
+References: bnc#44807 bnc#211997
+
+x
+
+Signed-off-by: Andrea Arcangeli <andrea@suse.de>
+Updated-by: Jeff Mahoney <jeffm@suse.com>
+---
+
+ arch/alpha/mm/fault.c                   |    2 -
+ arch/arm/mm/fault.c                     |    2 -
+ arch/cris/mm/fault.c                    |    2 -
+ arch/frv/mm/fault.c                     |    2 -
+ arch/ia64/mm/fault.c                    |    2 -
+ arch/m32r/mm/fault.c                    |    2 -
+ arch/m68k/mm/fault.c                    |    2 -
+ arch/mips/mm/fault.c                    |    2 -
+ arch/parisc/mm/fault.c                  |    2 -
+ arch/powerpc/mm/fault.c                 |    5 ++-
+ arch/powerpc/platforms/cell/spu_fault.c |    2 -
+ arch/s390/mm/fault.c                    |    2 -
+ arch/sh/mm/fault_32.c                   |    2 -
+ arch/sh/mm/tlbflush_64.c                |    2 -
+ arch/sparc/mm/fault.c                   |    4 +--
+ arch/sparc64/mm/fault.c                 |    2 -
+ arch/um/kernel/trap.c                   |    9 ++++--
+ arch/x86/kernel/sys_x86_64.c            |   10 ++++++-
+ arch/x86/mm/fault.c                     |   10 ++++++-
+ arch/xtensa/mm/fault.c                  |    2 -
+ include/linux/mm.h                      |    5 +++
+ kernel/sysctl.c                         |    8 ++++++
+ mm/mmap.c                               |   42 +++++++++++++++++++++++++-------
+ 23 files changed, 88 insertions(+), 35 deletions(-)
+
+--- a/arch/alpha/mm/fault.c
++++ b/arch/alpha/mm/fault.c
+@@ -123,7 +123,7 @@ do_page_fault(unsigned long address, uns
+               goto good_area;
+       if (!(vma->vm_flags & VM_GROWSDOWN))
+               goto bad_area;
+-      if (expand_stack(vma, address))
++      if (expand_stack(vma, address, NULL))
+               goto bad_area;
+       /* Ok, we have a good vm_area for this memory access, so
+--- a/arch/arm/mm/fault.c
++++ b/arch/arm/mm/fault.c
+@@ -233,7 +233,7 @@ out_of_memory:
+       goto survive;
+ check_stack:
+-      if (vma->vm_flags & VM_GROWSDOWN && !expand_stack(vma, addr))
++      if (vma->vm_flags & VM_GROWSDOWN && !expand_stack(vma, addr, NULL))
+               goto good_area;
+ out:
+       return fault;
+--- a/arch/cris/mm/fault.c
++++ b/arch/cris/mm/fault.c
+@@ -133,7 +133,7 @@ do_page_fault(unsigned long address, str
+               if (address + PAGE_SIZE < rdusp())
+                       goto bad_area;
+       }
+-      if (expand_stack(vma, address))
++      if (expand_stack(vma, address, NULL))
+               goto bad_area;
+       /*
+--- a/arch/frv/mm/fault.c
++++ b/arch/frv/mm/fault.c
+@@ -121,7 +121,7 @@ asmlinkage void do_page_fault(int datamm
+               }
+       }
+-      if (expand_stack(vma, ear0))
++      if (expand_stack(vma, ear0, NULL))
+               goto bad_area;
+ /*
+--- a/arch/ia64/mm/fault.c
++++ b/arch/ia64/mm/fault.c
+@@ -185,7 +185,7 @@ ia64_do_page_fault (unsigned long addres
+               if (REGION_NUMBER(address) != REGION_NUMBER(vma->vm_start)
+                   || REGION_OFFSET(address) >= RGN_MAP_LIMIT)
+                       goto bad_area;
+-              if (expand_stack(vma, address))
++              if (expand_stack(vma, address, NULL /* FIXME? */))
+                       goto bad_area;
+       } else {
+               vma = prev_vma;
+--- a/arch/m32r/mm/fault.c
++++ b/arch/m32r/mm/fault.c
+@@ -159,7 +159,7 @@ asmlinkage void do_page_fault(struct pt_
+                       goto bad_area;
+       }
+-      if (expand_stack(vma, address))
++      if (expand_stack(vma, address, NULL))
+               goto bad_area;
+ /*
+  * Ok, we have a good vm_area for this memory access, so
+--- a/arch/m68k/mm/fault.c
++++ b/arch/m68k/mm/fault.c
+@@ -121,7 +121,7 @@ int do_page_fault(struct pt_regs *regs,
+               if (address + 256 < rdusp())
+                       goto map_err;
+       }
+-      if (expand_stack(vma, address))
++      if (expand_stack(vma, address, NULL))
+               goto map_err;
+ /*
+--- a/arch/mips/mm/fault.c
++++ b/arch/mips/mm/fault.c
+@@ -80,7 +80,7 @@ asmlinkage void do_page_fault(struct pt_
+               goto good_area;
+       if (!(vma->vm_flags & VM_GROWSDOWN))
+               goto bad_area;
+-      if (expand_stack(vma, address))
++      if (expand_stack(vma, address, NULL))
+               goto bad_area;
+ /*
+  * Ok, we have a good vm_area for this memory access, so
+--- a/arch/parisc/mm/fault.c
++++ b/arch/parisc/mm/fault.c
+@@ -196,7 +196,7 @@ good_area:
+ check_expansion:
+       vma = prev_vma;
+-      if (vma && (expand_stack(vma, address) == 0))
++      if (vma && (expand_stack(vma, address, NULL) == 0))
+               goto good_area;
+ /*
+--- a/arch/powerpc/mm/fault.c
++++ b/arch/powerpc/mm/fault.c
+@@ -116,7 +116,7 @@ static int store_updates_sp(struct pt_re
+ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address,
+                           unsigned long error_code)
+ {
+-      struct vm_area_struct * vma;
++      struct vm_area_struct * vma, * prev_vma;
+       struct mm_struct *mm = current->mm;
+       siginfo_t info;
+       int code = SEGV_MAPERR;
+@@ -230,7 +230,8 @@ int __kprobes do_page_fault(struct pt_re
+                   && (!user_mode(regs) || !store_updates_sp(regs)))
+                       goto bad_area;
+       }
+-      if (expand_stack(vma, address))
++      find_vma_prev(mm, address, &prev_vma);
++      if (expand_stack(vma, address, prev_vma))
+               goto bad_area;
+ good_area:
+--- a/arch/powerpc/platforms/cell/spu_fault.c
++++ b/arch/powerpc/platforms/cell/spu_fault.c
+@@ -59,7 +59,7 @@ int spu_handle_mm_fault(struct mm_struct
+               goto good_area;
+       if (!(vma->vm_flags & VM_GROWSDOWN))
+               goto bad_area;
+-      if (expand_stack(vma, ea))
++      if (expand_stack(vma, ea, NULL))
+               goto bad_area;
+ good_area:
+       is_write = dsisr & MFC_DSISR_ACCESS_PUT;
+--- a/arch/s390/mm/fault.c
++++ b/arch/s390/mm/fault.c
+@@ -350,7 +350,7 @@ do_exception(struct pt_regs *regs, unsig
+               goto good_area;
+       if (!(vma->vm_flags & VM_GROWSDOWN))
+               goto bad_area;
+-      if (expand_stack(vma, address))
++      if (expand_stack(vma, address, NULL /* FIXME? */))
+               goto bad_area;
+ /*
+  * Ok, we have a good vm_area for this memory access, so
+--- a/arch/sh/mm/fault_32.c
++++ b/arch/sh/mm/fault_32.c
+@@ -108,7 +108,7 @@ asmlinkage void __kprobes do_page_fault(
+               goto good_area;
+       if (!(vma->vm_flags & VM_GROWSDOWN))
+               goto bad_area;
+-      if (expand_stack(vma, address))
++      if (expand_stack(vma, address, NULL))
+               goto bad_area;
+ /*
+  * Ok, we have a good vm_area for this memory access, so
+--- a/arch/sh/mm/tlbflush_64.c
++++ b/arch/sh/mm/tlbflush_64.c
+@@ -153,7 +153,7 @@ asmlinkage void do_page_fault(struct pt_
+ #endif
+               goto bad_area;
+       }
+-      if (expand_stack(vma, address)) {
++      if (expand_stack(vma, address, NULL)) {
+ #ifdef DEBUG_FAULT
+               print_task(tsk);
+               printk("%s:%d fault, address is 0x%08x PC %016Lx textaccess %d writeaccess %d\n",
+--- a/arch/sparc/mm/fault.c
++++ b/arch/sparc/mm/fault.c
+@@ -219,7 +219,7 @@ asmlinkage void do_sparc_fault(struct pt
+               goto good_area;
+       if(!(vma->vm_flags & VM_GROWSDOWN))
+               goto bad_area;
+-      if(expand_stack(vma, address))
++      if(expand_stack(vma, address, NULL))
+               goto bad_area;
+       /*
+        * Ok, we have a good vm_area for this memory access, so
+@@ -472,7 +472,7 @@ static void force_user_fault(unsigned lo
+               goto good_area;
+       if(!(vma->vm_flags & VM_GROWSDOWN))
+               goto bad_area;
+-      if(expand_stack(vma, address))
++      if(expand_stack(vma, address, NULL))
+               goto bad_area;
+ good_area:
+       info.si_code = SEGV_ACCERR;
+--- a/arch/sparc64/mm/fault.c
++++ b/arch/sparc64/mm/fault.c
+@@ -367,7 +367,7 @@ continue_fault:
+                               goto bad_area;
+               }
+       }
+-      if (expand_stack(vma, address))
++      if (expand_stack(vma, address, NULL))
+               goto bad_area;
+       /*
+        * Ok, we have a good vm_area for this memory access, so
+--- a/arch/um/kernel/trap.c
++++ b/arch/um/kernel/trap.c
+@@ -24,7 +24,7 @@ int handle_page_fault(unsigned long addr
+                     int is_write, int is_user, int *code_out)
+ {
+       struct mm_struct *mm = current->mm;
+-      struct vm_area_struct *vma;
++      struct vm_area_struct *vma, *prev_vma;
+       pgd_t *pgd;
+       pud_t *pud;
+       pmd_t *pmd;
+@@ -50,8 +50,11 @@ int handle_page_fault(unsigned long addr
+               goto out;
+       else if (is_user && !ARCH_IS_STACKGROW(address))
+               goto out;
+-      else if (expand_stack(vma, address))
+-              goto out;
++      else {
++              find_vma_prev(mm, address, &prev_vma);
++              if(expand_stack(vma, address, prev_vma))
++                      goto out;
++      }
+ good_area:
+       *code_out = SEGV_ACCERR;
+--- a/arch/x86/kernel/sys_x86_64.c
++++ b/arch/x86/kernel/sys_x86_64.c
+@@ -106,6 +106,7 @@ arch_get_unmapped_area(struct file *filp
+ full_search:
+       for (vma = find_vma(mm, addr); ; vma = vma->vm_next) {
++              unsigned long __heap_stack_gap;
+               /* At this point:  (!vma || addr < vma->vm_end). */
+               if (end - len < addr) {
+                       /*
+@@ -119,7 +120,14 @@ full_search:
+                       }
+                       return -ENOMEM;
+               }
+-              if (!vma || addr + len <= vma->vm_start) {
++              if (!vma)
++                      goto got_it;
++              __heap_stack_gap = 0;
++              if (vma->vm_flags & VM_GROWSDOWN)
++                      __heap_stack_gap = min(end-(addr+len),
++                                             (unsigned long) heap_stack_gap << PAGE_SHIFT);
++              if (addr + len + __heap_stack_gap <= vma->vm_start) {
++              got_it:
+                       /*
+                        * Remember the place where we stopped the search:
+                        */
+--- a/arch/x86/mm/fault.c
++++ b/arch/x86/mm/fault.c
+@@ -585,7 +585,7 @@ void __kprobes do_page_fault(struct pt_r
+ {
+       struct task_struct *tsk;
+       struct mm_struct *mm;
+-      struct vm_area_struct *vma;
++      struct vm_area_struct *vma, *prev_vma;
+       unsigned long address;
+       int write, si_code;
+       int fault;
+@@ -719,7 +719,13 @@ again:
+               if (address + 65536 + 32 * sizeof(unsigned long) < regs->sp)
+                       goto bad_area;
+       }
+-      if (expand_stack(vma, address))
++      /*
++       * find_vma_prev is just a bit slower, because it cannot
++       * use the mmap_cache, so we run it only in the growsdown
++       * slow path and we leave find_vma in the fast path.
++       */
++      find_vma_prev(current->mm, address, &prev_vma);
++      if (expand_stack(vma, address, prev_vma))
+               goto bad_area;
+ /*
+  * Ok, we have a good vm_area for this memory access, so
+--- a/arch/xtensa/mm/fault.c
++++ b/arch/xtensa/mm/fault.c
+@@ -80,7 +80,7 @@ void do_page_fault(struct pt_regs *regs)
+               goto good_area;
+       if (!(vma->vm_flags & VM_GROWSDOWN))
+               goto bad_area;
+-      if (expand_stack(vma, address))
++      if (expand_stack(vma, address, NULL))
+               goto bad_area;
+       /* Ok, we have a good vm_area for this memory access, so
+--- a/include/linux/mm.h
++++ b/include/linux/mm.h
+@@ -1208,7 +1208,10 @@ void page_cache_async_readahead(struct a
+ unsigned long max_sane_readahead(unsigned long nr);
+ /* Do stack extension */
+-extern int expand_stack(struct vm_area_struct *vma, unsigned long address);
++#define EXPAND_STACK_HAS_3_ARGS
++extern int heap_stack_gap;
++extern int expand_stack(struct vm_area_struct * vma, unsigned long address,
++                      struct vm_area_struct * prev_vma);
+ #ifdef CONFIG_IA64
+ extern int expand_upwards(struct vm_area_struct *vma, unsigned long address);
+ #endif
+--- a/kernel/sysctl.c
++++ b/kernel/sysctl.c
+@@ -1208,6 +1208,14 @@ static struct ctl_table vm_table[] = {
+               .extra2         = &one,
+       },
+ #endif
++      {
++              .ctl_name       = CTL_UNNUMBERED,
++              .procname       = "heap-stack-gap",
++              .data           = &heap_stack_gap,
++              .maxlen         = sizeof(int),
++              .mode           = 0644,
++              .proc_handler   = &proc_dointvec,
++      },
+ /*
+  * NOTE: do not add new entries to this table unless you have read
+  * Documentation/sysctl/ctl_unnumbered.txt
+--- a/mm/mmap.c
++++ b/mm/mmap.c
+@@ -85,6 +85,7 @@ int sysctl_overcommit_memory = OVERCOMMI
+ int sysctl_overcommit_ratio = 50;     /* default is 50% */
+ int sysctl_max_map_count __read_mostly = DEFAULT_MAX_MAP_COUNT;
+ atomic_long_t vm_committed_space = ATOMIC_LONG_INIT(0);
++int heap_stack_gap = 1;
+ /*
+  * Check that a process has enough memory to allocate a new virtual
+@@ -1291,6 +1292,7 @@ arch_get_unmapped_area(struct file *filp
+ full_search:
+       for (vma = find_vma(mm, addr); ; vma = vma->vm_next) {
+               /* At this point:  (!vma || addr < vma->vm_end). */
++              unsigned long __heap_stack_gap;
+               if (TASK_SIZE - len < addr) {
+                       /*
+                        * Start a new search - just in case we missed
+@@ -1304,7 +1306,14 @@ full_search:
+                       }
+                       return -ENOMEM;
+               }
+-              if (!vma || addr + len <= vma->vm_start) {
++              if (!vma)
++                      goto got_it;
++              __heap_stack_gap = 0;
++              if (vma->vm_flags & VM_GROWSDOWN)
++                      __heap_stack_gap = min(TASK_SIZE-(addr+len),
++                                             (unsigned long) heap_stack_gap << PAGE_SHIFT);
++              if (addr + len + __heap_stack_gap <= vma->vm_start) {
++              got_it:
+                       /*
+                        * Remember the place where we stopped the search:
+                        */
+@@ -1633,11 +1642,9 @@ int expand_upwards(struct vm_area_struct
+ }
+ #endif /* CONFIG_STACK_GROWSUP || CONFIG_IA64 */
+-/*
+- * vma is the first one with address < vma->vm_start.  Have to extend vma.
+- */
+ static inline int expand_downwards(struct vm_area_struct *vma,
+-                                 unsigned long address)
++                                 unsigned long address,
++                                 struct vm_area_struct *prev_vma)
+ {
+       int error;
+@@ -1665,6 +1672,13 @@ static inline int expand_downwards(struc
+       if (address < vma->vm_start) {
+               unsigned long size, grow;
++              error = -ENOMEM;
++              if (prev_vma) {
++                      unsigned long __heap_stack_gap = min(TASK_SIZE-prev_vma->vm_end,
++                                                           (unsigned long) heap_stack_gap << PAGE_SHIFT);
++                      if (unlikely(prev_vma->vm_end + __heap_stack_gap > address))
++                              goto out_unlock;
++              }
+               size = vma->vm_end - address;
+               grow = (vma->vm_start - address) >> PAGE_SHIFT;
+@@ -1674,6 +1688,7 @@ static inline int expand_downwards(struc
+                       vma->vm_pgoff -= grow;
+               }
+       }
++ out_unlock:
+       anon_vma_unlock(vma);
+       return error;
+ }
+@@ -1684,8 +1699,16 @@ int expand_stack_downwards(struct vm_are
+ }
+ #ifdef CONFIG_STACK_GROWSUP
+-int expand_stack(struct vm_area_struct *vma, unsigned long address)
++int expand_stack(struct vm_area_struct * vma, unsigned long address,
++               struct vm_area_struct * prev_vma)
+ {
++      /*
++       * If you re-use the heap-stack-gap for a growsup stack you
++       * should implement the feature for growsup too and remove
++       * this WARN_ON.
++       */
++      WARN_ON(prev_vma);
++
+       return expand_upwards(vma, address);
+ }
+@@ -1698,7 +1721,7 @@ find_extend_vma(struct mm_struct *mm, un
+       vma = find_vma_prev(mm, addr, &prev);
+       if (vma && (vma->vm_start <= addr))
+               return vma;
+-      if (!prev || expand_stack(prev, addr))
++      if (!prev || expand_stack(prev, addr, NULL))
+               return NULL;
+       if (prev->vm_flags & VM_LOCKED)
+               make_pages_present(addr, prev->vm_end);
+@@ -1713,7 +1736,7 @@ int expand_stack(struct vm_area_struct *
+ struct vm_area_struct *
+ find_extend_vma(struct mm_struct * mm, unsigned long addr)
+ {
+-      struct vm_area_struct * vma;
++      struct vm_area_struct * vma, * prev_vma;
+       unsigned long start;
+       addr &= PAGE_MASK;
+@@ -1725,7 +1748,8 @@ find_extend_vma(struct mm_struct * mm, u
+       if (!(vma->vm_flags & VM_GROWSDOWN))
+               return NULL;
+       start = vma->vm_start;
+-      if (expand_stack(vma, addr))
++      find_vma_prev(mm, addr, &prev_vma);
++      if (expand_stack(vma, addr, prev_vma))
+               return NULL;
+       if (vma->vm_flags & VM_LOCKED)
+               make_pages_present(addr, start);