
From: Andrea Arcangeli <andrea@novell.com>

This patch enforces a gap between heap and stack, both on the mmap side (for
heap) and on the growsdown page faults for stack.  The gap is in page units
and it's sysctl configurable.

This is needed for some critical app, that wants an higher degree of
protection against potential stack overflows from the kernel.  This is mostly
a 32bit matter of course, since on 32bit those apps are using a few gigs of
heap and they get as near as they can to the stack (but if something goes
wrong a page fault must happen).

The default value of 1 avoids userspace apps like java to break, but those
apps will of course set by hand in the rc.d scripts a much higher value.  1 is
a sane default, if you want to tweak the default with mainline inclusion
that's fine with me.  the sysctl can always be disabled by setting it to 0 and
then nobody will notice.

Feature is fully enabled on x86* and ppc*.  No idea about the ia64 and s390x
layouts but they've presumably a lot more address space not to care about this
(this is primarly needed on 32bit apps).  

I didn't check the topdown model, in theory it should be extended to cover
that too, this is only working for the legacy model right now because those
apps aren't going to use topdown anyways.

Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/arch/alpha/mm/fault.c           |    2 -
 25-akpm/arch/arm/mm/fault.c             |    2 -
 25-akpm/arch/arm26/mm/fault.c           |    2 -
 25-akpm/arch/cris/mm/fault.c            |    2 -
 25-akpm/arch/i386/mm/fault.c            |   10 +++++++--
 25-akpm/arch/ia64/mm/fault.c            |    2 -
 25-akpm/arch/m68k/mm/fault.c            |    2 -
 25-akpm/arch/mips/mm/fault.c            |    2 -
 25-akpm/arch/parisc/mm/fault.c          |    2 -
 25-akpm/arch/ppc/mm/fault.c             |    5 ++--
 25-akpm/arch/ppc64/mm/fault.c           |    5 ++--
 25-akpm/arch/ppc64/mm/hugetlbpage.c     |    9 +++++++-
 25-akpm/arch/s390/mm/fault.c            |    2 -
 25-akpm/arch/sh/mm/fault.c              |    2 -
 25-akpm/arch/sh64/mm/fault.c            |    2 -
 25-akpm/arch/sparc/mm/fault.c           |    4 +--
 25-akpm/arch/sparc64/mm/fault.c         |    2 -
 25-akpm/arch/um/kernel/trap_kern.c      |    9 +++++---
 25-akpm/arch/x86_64/kernel/sys_x86_64.c |    9 +++++++-
 25-akpm/arch/x86_64/mm/fault.c          |   10 +++++++--
 25-akpm/include/linux/mm.h              |    4 ++-
 25-akpm/include/linux/sysctl.h          |    1 
 25-akpm/kernel/sysctl.c                 |    8 +++++++
 25-akpm/mm/mmap.c                       |   35 +++++++++++++++++++++++---------
 24 files changed, 96 insertions(+), 37 deletions(-)

diff -puN arch/alpha/mm/fault.c~enforce-a-gap-between-heap-and-stack arch/alpha/mm/fault.c
--- 25/arch/alpha/mm/fault.c~enforce-a-gap-between-heap-and-stack	2004-11-07 22:38:00.070401592 -0800
+++ 25-akpm/arch/alpha/mm/fault.c	2004-11-07 22:38:00.106396120 -0800
@@ -125,7 +125,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
diff -puN arch/arm26/mm/fault.c~enforce-a-gap-between-heap-and-stack arch/arm26/mm/fault.c
--- 25/arch/arm26/mm/fault.c~enforce-a-gap-between-heap-and-stack	2004-11-07 22:38:00.072401288 -0800
+++ 25-akpm/arch/arm26/mm/fault.c	2004-11-07 22:38:00.107395968 -0800
@@ -197,7 +197,7 @@ survive:
 	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;
diff -puN arch/arm/mm/fault.c~enforce-a-gap-between-heap-and-stack arch/arm/mm/fault.c
--- 25/arch/arm/mm/fault.c~enforce-a-gap-between-heap-and-stack	2004-11-07 22:38:00.073401136 -0800
+++ 25-akpm/arch/arm/mm/fault.c	2004-11-07 22:38:00.107395968 -0800
@@ -208,7 +208,7 @@ survive:
 	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;
diff -puN arch/cris/mm/fault.c~enforce-a-gap-between-heap-and-stack arch/cris/mm/fault.c
--- 25/arch/cris/mm/fault.c~enforce-a-gap-between-heap-and-stack	2004-11-07 22:38:00.074400984 -0800
+++ 25-akpm/arch/cris/mm/fault.c	2004-11-07 22:38:00.108395816 -0800
@@ -207,7 +207,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;
 
 	/*
diff -puN arch/i386/mm/fault.c~enforce-a-gap-between-heap-and-stack arch/i386/mm/fault.c
--- 25/arch/i386/mm/fault.c~enforce-a-gap-between-heap-and-stack	2004-11-07 22:38:00.076400680 -0800
+++ 25-akpm/arch/i386/mm/fault.c	2004-11-07 22:38:00.109395664 -0800
@@ -217,7 +217,7 @@ fastcall void do_page_fault(struct pt_re
 {
 	struct task_struct *tsk;
 	struct mm_struct *mm;
-	struct vm_area_struct * vma;
+	struct vm_area_struct *vma, *prev_vma;
 	unsigned long address;
 	unsigned long page;
 	int write;
@@ -308,7 +308,13 @@ fastcall void do_page_fault(struct pt_re
 		if (address + 32 < regs->esp)
 			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
diff -puN arch/ia64/mm/fault.c~enforce-a-gap-between-heap-and-stack arch/ia64/mm/fault.c
--- 25/arch/ia64/mm/fault.c~enforce-a-gap-between-heap-and-stack	2004-11-07 22:38:00.077400528 -0800
+++ 25-akpm/arch/ia64/mm/fault.c	2004-11-07 22:38:00.109395664 -0800
@@ -165,7 +165,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;
diff -puN arch/m68k/mm/fault.c~enforce-a-gap-between-heap-and-stack arch/m68k/mm/fault.c
--- 25/arch/m68k/mm/fault.c~enforce-a-gap-between-heap-and-stack	2004-11-07 22:38:00.078400376 -0800
+++ 25-akpm/arch/m68k/mm/fault.c	2004-11-07 22:38:00.109395664 -0800
@@ -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;
 
 /*
diff -puN arch/mips/mm/fault.c~enforce-a-gap-between-heap-and-stack arch/mips/mm/fault.c
--- 25/arch/mips/mm/fault.c~enforce-a-gap-between-heap-and-stack	2004-11-07 22:38:00.080400072 -0800
+++ 25-akpm/arch/mips/mm/fault.c	2004-11-07 22:38:00.110395512 -0800
@@ -75,7 +75,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
diff -puN arch/parisc/mm/fault.c~enforce-a-gap-between-heap-and-stack arch/parisc/mm/fault.c
--- 25/arch/parisc/mm/fault.c~enforce-a-gap-between-heap-and-stack	2004-11-07 22:38:00.082399768 -0800
+++ 25-akpm/arch/parisc/mm/fault.c	2004-11-07 22:38:00.110395512 -0800
@@ -199,7 +199,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;
 
 /*
diff -puN arch/ppc64/mm/fault.c~enforce-a-gap-between-heap-and-stack arch/ppc64/mm/fault.c
--- 25/arch/ppc64/mm/fault.c~enforce-a-gap-between-heap-and-stack	2004-11-07 22:38:00.083399616 -0800
+++ 25-akpm/arch/ppc64/mm/fault.c	2004-11-07 22:38:00.111395360 -0800
@@ -86,7 +86,7 @@ static int store_updates_sp(struct pt_re
 int 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;
 	unsigned long code = SEGV_MAPERR;
@@ -185,7 +185,8 @@ int do_page_fault(struct pt_regs *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:
diff -puN arch/ppc64/mm/hugetlbpage.c~enforce-a-gap-between-heap-and-stack arch/ppc64/mm/hugetlbpage.c
--- 25/arch/ppc64/mm/hugetlbpage.c~enforce-a-gap-between-heap-and-stack	2004-11-07 22:38:00.085399312 -0800
+++ 25-akpm/arch/ppc64/mm/hugetlbpage.c	2004-11-07 22:38:00.111395360 -0800
@@ -516,6 +516,7 @@ unsigned long arch_get_unmapped_area(str
 full_search:
 	vma = find_vma(mm, addr);
 	while (TASK_SIZE - len >= addr) {
+		unsigned long __heap_stack_gap;
 		BUG_ON(vma && (addr >= vma->vm_end));
 
 		if (touches_hugepage_low_range(addr, len)) {
@@ -528,7 +529,13 @@ full_search:
 			vma = find_vma(mm, addr);
 			continue;
 		}
-		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 = heap_stack_gap << PAGE_SHIFT;
+		if (addr + len + __heap_stack_gap <= vma->vm_start) {
+got_it:
 			/*
 			 * Remember the place where we stopped the search:
 			 */
diff -puN arch/ppc/mm/fault.c~enforce-a-gap-between-heap-and-stack arch/ppc/mm/fault.c
--- 25/arch/ppc/mm/fault.c~enforce-a-gap-between-heap-and-stack	2004-11-07 22:38:00.086399160 -0800
+++ 25-akpm/arch/ppc/mm/fault.c	2004-11-07 22:38:00.112395208 -0800
@@ -95,7 +95,7 @@ static int store_updates_sp(struct pt_re
 int 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;
@@ -175,7 +175,8 @@ int do_page_fault(struct pt_regs *regs, 
 		    && (!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:
diff -puN arch/s390/mm/fault.c~enforce-a-gap-between-heap-and-stack arch/s390/mm/fault.c
--- 25/arch/s390/mm/fault.c~enforce-a-gap-between-heap-and-stack	2004-11-07 22:38:00.087399008 -0800
+++ 25-akpm/arch/s390/mm/fault.c	2004-11-07 22:38:00.113395056 -0800
@@ -225,7 +225,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
diff -puN arch/sh64/mm/fault.c~enforce-a-gap-between-heap-and-stack arch/sh64/mm/fault.c
--- 25/arch/sh64/mm/fault.c~enforce-a-gap-between-heap-and-stack	2004-11-07 22:38:00.089398704 -0800
+++ 25-akpm/arch/sh64/mm/fault.c	2004-11-07 22:38:00.113395056 -0800
@@ -188,7 +188,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",
diff -puN arch/sh/mm/fault.c~enforce-a-gap-between-heap-and-stack arch/sh/mm/fault.c
--- 25/arch/sh/mm/fault.c~enforce-a-gap-between-heap-and-stack	2004-11-07 22:38:00.090398552 -0800
+++ 25-akpm/arch/sh/mm/fault.c	2004-11-07 22:38:00.114394904 -0800
@@ -69,7 +69,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
diff -puN arch/sparc64/mm/fault.c~enforce-a-gap-between-heap-and-stack arch/sparc64/mm/fault.c
--- 25/arch/sparc64/mm/fault.c~enforce-a-gap-between-heap-and-stack	2004-11-07 22:38:00.091398400 -0800
+++ 25-akpm/arch/sparc64/mm/fault.c	2004-11-07 22:38:00.114394904 -0800
@@ -410,7 +410,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
diff -puN arch/sparc/mm/fault.c~enforce-a-gap-between-heap-and-stack arch/sparc/mm/fault.c
--- 25/arch/sparc/mm/fault.c~enforce-a-gap-between-heap-and-stack	2004-11-07 22:38:00.093398096 -0800
+++ 25-akpm/arch/sparc/mm/fault.c	2004-11-07 22:38:00.115394752 -0800
@@ -271,7 +271,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
@@ -524,7 +524,7 @@ inline 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;
diff -puN arch/um/kernel/trap_kern.c~enforce-a-gap-between-heap-and-stack arch/um/kernel/trap_kern.c
--- 25/arch/um/kernel/trap_kern.c~enforce-a-gap-between-heap-and-stack	2004-11-07 22:38:00.094397944 -0800
+++ 25-akpm/arch/um/kernel/trap_kern.c	2004-11-07 22:38:00.115394752 -0800
@@ -30,7 +30,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;
 	pmd_t *pmd;
 	pte_t *pte;
@@ -46,8 +46,11 @@ int handle_page_fault(unsigned long addr
 		goto good_area;
 	else if(!(vma->vm_flags & VM_GROWSDOWN)) 
 		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;
diff -puN arch/x86_64/kernel/sys_x86_64.c~enforce-a-gap-between-heap-and-stack arch/x86_64/kernel/sys_x86_64.c
--- 25/arch/x86_64/kernel/sys_x86_64.c~enforce-a-gap-between-heap-and-stack	2004-11-07 22:38:00.095397792 -0800
+++ 25-akpm/arch/x86_64/kernel/sys_x86_64.c	2004-11-07 22:38:00.116394600 -0800
@@ -119,6 +119,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) {
 			/*
@@ -131,7 +132,13 @@ 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 = heap_stack_gap << PAGE_SHIFT;
+		if (addr + len + __heap_stack_gap <= vma->vm_start) {
+got_it:
 			/*
 			 * Remember the place where we stopped the search:
 			 */
diff -puN arch/x86_64/mm/fault.c~enforce-a-gap-between-heap-and-stack arch/x86_64/mm/fault.c
--- 25/arch/x86_64/mm/fault.c~enforce-a-gap-between-heap-and-stack	2004-11-07 22:38:00.097397488 -0800
+++ 25-akpm/arch/x86_64/mm/fault.c	2004-11-07 22:38:00.117394448 -0800
@@ -296,7 +296,7 @@ asmlinkage void do_page_fault(struct pt_
 {
 	struct task_struct *tsk;
 	struct mm_struct *mm;
-	struct vm_area_struct * vma;
+	struct vm_area_struct *vma, *prev_vma;
 	unsigned long address;
 	const struct exception_table_entry *fixup;
 	int write;
@@ -403,7 +403,13 @@ asmlinkage void do_page_fault(struct pt_
 		if (address + 128 < regs->rsp)
 			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
diff -puN include/linux/mm.h~enforce-a-gap-between-heap-and-stack include/linux/mm.h
--- 25/include/linux/mm.h~enforce-a-gap-between-heap-and-stack	2004-11-07 22:38:00.098397336 -0800
+++ 25-akpm/include/linux/mm.h	2004-11-07 22:38:00.118394296 -0800
@@ -768,7 +768,9 @@ void handle_ra_miss(struct address_space
 unsigned long max_sane_readahead(unsigned long nr);
 
 /* Do stack extension */
-extern int expand_stack(struct vm_area_struct * vma, unsigned long address);
+extern int heap_stack_gap;
+extern int expand_stack(struct vm_area_struct *vma, unsigned long address,
+			struct vm_area_struct *prev_vma);
 
 /* Look up the first VMA which satisfies  addr < vm_end,  NULL if none. */
 extern struct vm_area_struct * find_vma(struct mm_struct * mm, unsigned long addr);
diff -puN include/linux/sysctl.h~enforce-a-gap-between-heap-and-stack include/linux/sysctl.h
--- 25/include/linux/sysctl.h~enforce-a-gap-between-heap-and-stack	2004-11-07 22:38:00.100397032 -0800
+++ 25-akpm/include/linux/sysctl.h	2004-11-07 22:38:00.119394144 -0800
@@ -168,6 +168,7 @@ enum
 	VM_VFS_CACHE_PRESSURE=26, /* dcache/icache reclaim pressure */
 	VM_LEGACY_VA_LAYOUT=27, /* legacy/compatibility virtual address space layout */
 	VM_SWAP_TOKEN_TIMEOUT=28, /* default time for token time out */
+ 	VM_HEAP_STACK_GAP=29,	/* int: page gap between heap and stack */
 };
 
 
diff -puN kernel/sysctl.c~enforce-a-gap-between-heap-and-stack kernel/sysctl.c
--- 25/kernel/sysctl.c~enforce-a-gap-between-heap-and-stack	2004-11-07 22:38:00.101396880 -0800
+++ 25-akpm/kernel/sysctl.c	2004-11-07 22:38:00.120393992 -0800
@@ -816,6 +816,14 @@ static ctl_table vm_table[] = {
 		.strategy	= &sysctl_jiffies,
 	},
 #endif
+	{
+		.ctl_name	= VM_HEAP_STACK_GAP,
+		.procname	= "heap-stack-gap",
+		.data		= &heap_stack_gap,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec,
+	},
 	{ .ctl_name = 0 }
 };
 
diff -puN mm/mmap.c~enforce-a-gap-between-heap-and-stack mm/mmap.c
--- 25/mm/mmap.c~enforce-a-gap-between-heap-and-stack	2004-11-07 22:38:00.103396576 -0800
+++ 25-akpm/mm/mmap.c	2004-11-07 22:38:00.122393688 -0800
@@ -58,6 +58,7 @@ int sysctl_overcommit_memory = OVERCOMMI
 int sysctl_overcommit_ratio = 50;	/* default is 50% */
 int sysctl_max_map_count = DEFAULT_MAX_MAP_COUNT;
 atomic_t vm_committed_space = ATOMIC_INIT(0);
+int heap_stack_gap = 1;
 
 EXPORT_SYMBOL(sysctl_overcommit_memory);
 EXPORT_SYMBOL(sysctl_overcommit_ratio);
@@ -1086,6 +1087,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
@@ -1097,7 +1099,13 @@ 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 = heap_stack_gap << PAGE_SHIFT;
+		if (addr + len + __heap_stack_gap <= vma->vm_start) {
+got_it:
 			/*
 			 * Remember the place where we stopped the search:
 			 */
@@ -1332,13 +1340,17 @@ out:
 }
 
 #ifdef CONFIG_STACK_GROWSUP
-/*
- * vma is the first one with address > vma->vm_end.  Have to extend vma.
- */
-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)
 {
 	unsigned long grow;
 
+	/*
+	 * If you re-use the heap-stack-gap for a growsup stack you
+	 * should remove this WARN_ON.
+	 */
+	WARN_ON(prev_vma);
+
 	if (!(vma->vm_flags & VM_GROWSUP))
 		return -EFAULT;
 
@@ -1390,7 +1402,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);
@@ -1401,7 +1413,8 @@ find_extend_vma(struct mm_struct *mm, un
 /*
  * vma is the first one with address < vma->vm_start.  Have to extend vma.
  */
-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)
 {
 	unsigned long grow;
 
@@ -1419,10 +1432,13 @@ int expand_stack(struct vm_area_struct *
 	 * anon_vma lock to serialize against concurrent expand_stacks.
 	 */
 	address &= PAGE_MASK;
+	if (prev_vma && unlikely(prev_vma->vm_end + (heap_stack_gap << PAGE_SHIFT) > address))
+		goto out_unlock;
 	grow = (vma->vm_start - address) >> PAGE_SHIFT;
 
 	/* Overcommit.. */
 	if (security_vm_enough_memory(grow)) {
+out_unlock:
 		anon_vma_unlock(vma);
 		return -ENOMEM;
 	}
@@ -1447,7 +1463,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;
@@ -1459,7 +1475,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);
_
