Signed-Off-By: Andrea Arcangeli <andrea@novell.com>

Index: linux-2.5/arch/alpha/mm/fault.c
===================================================================
RCS file: /home/andrea/crypto/cvs/linux-2.5/arch/alpha/mm/fault.c,v
retrieving revision 1.15
diff -u -p -r1.15 fault.c
--- linux-2.5/arch/alpha/mm/fault.c	23 Sep 2004 05:59:47 -0000	1.15
+++ linux-2.5/arch/alpha/mm/fault.c	25 Sep 2004 01:46:10 -0000
@@ -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
Index: linux-2.5/arch/arm/mm/fault.c
===================================================================
RCS file: /home/andrea/crypto/cvs/linux-2.5/arch/arm/mm/fault.c,v
retrieving revision 1.5
diff -u -p -r1.5 fault.c
--- linux-2.5/arch/arm/mm/fault.c	6 Aug 2004 23:06:02 -0000	1.5
+++ linux-2.5/arch/arm/mm/fault.c	25 Sep 2004 01:46:10 -0000
@@ -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;
Index: linux-2.5/arch/arm26/mm/fault.c
===================================================================
RCS file: /home/andrea/crypto/cvs/linux-2.5/arch/arm26/mm/fault.c,v
retrieving revision 1.3
diff -u -p -r1.3 fault.c
--- linux-2.5/arch/arm26/mm/fault.c	7 Sep 2003 23:53:07 -0000	1.3
+++ linux-2.5/arch/arm26/mm/fault.c	25 Sep 2004 01:46:10 -0000
@@ -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;
Index: linux-2.5/arch/cris/mm/fault.c
===================================================================
RCS file: /home/andrea/crypto/cvs/linux-2.5/arch/cris/mm/fault.c,v
retrieving revision 1.14
diff -u -p -r1.14 fault.c
--- linux-2.5/arch/cris/mm/fault.c	1 Jun 2004 15:52:29 -0000	1.14
+++ linux-2.5/arch/cris/mm/fault.c	25 Sep 2004 01:46:10 -0000
@@ -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;
 
 	/*
Index: linux-2.5/arch/i386/mm/fault.c
===================================================================
RCS file: /home/andrea/crypto/cvs/linux-2.5/arch/i386/mm/fault.c,v
retrieving revision 1.41
diff -u -p -r1.41 fault.c
--- linux-2.5/arch/i386/mm/fault.c	3 Oct 2004 16:14:25 -0000	1.41
+++ linux-2.5/arch/i386/mm/fault.c	4 Oct 2004 17:58:57 -0000
@@ -217,7 +217,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;
 	unsigned long page;
 	int write;
@@ -308,7 +308,13 @@ asmlinkage void do_page_fault(struct pt_
 		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
Index: linux-2.5/arch/ia64/mm/fault.c
===================================================================
RCS file: /home/andrea/crypto/cvs/linux-2.5/arch/ia64/mm/fault.c,v
retrieving revision 1.21
diff -u -p -r1.21 fault.c
--- linux-2.5/arch/ia64/mm/fault.c	8 Oct 2004 02:57:28 -0000	1.21
+++ linux-2.5/arch/ia64/mm/fault.c	11 Oct 2004 14:48:01 -0000
@@ -164,7 +164,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;
Index: linux-2.5/arch/m68k/mm/fault.c
===================================================================
RCS file: /home/andrea/crypto/cvs/linux-2.5/arch/m68k/mm/fault.c,v
retrieving revision 1.6
diff -u -p -r1.6 fault.c
--- linux-2.5/arch/m68k/mm/fault.c	11 May 2004 14:54:30 -0000	1.6
+++ linux-2.5/arch/m68k/mm/fault.c	25 Sep 2004 01:46:10 -0000
@@ -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;
 
 /*
Index: linux-2.5/arch/mips/mm/fault.c
===================================================================
RCS file: /home/andrea/crypto/cvs/linux-2.5/arch/mips/mm/fault.c,v
retrieving revision 1.12
diff -u -p -r1.12 fault.c
--- linux-2.5/arch/mips/mm/fault.c	8 Sep 2004 14:48:45 -0000	1.12
+++ linux-2.5/arch/mips/mm/fault.c	25 Sep 2004 01:46:10 -0000
@@ -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
Index: linux-2.5/arch/parisc/mm/fault.c
===================================================================
RCS file: /home/andrea/crypto/cvs/linux-2.5/arch/parisc/mm/fault.c,v
retrieving revision 1.5
diff -u -p -r1.5 fault.c
--- linux-2.5/arch/parisc/mm/fault.c	13 Jan 2003 21:24:33 -0000	1.5
+++ linux-2.5/arch/parisc/mm/fault.c	25 Sep 2004 01:46:10 -0000
@@ -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;
 
 /*
Index: linux-2.5/arch/ppc/mm/fault.c
===================================================================
RCS file: /home/andrea/crypto/cvs/linux-2.5/arch/ppc/mm/fault.c,v
retrieving revision 1.22
diff -u -p -r1.22 fault.c
--- linux-2.5/arch/ppc/mm/fault.c	27 Jul 2004 04:02:20 -0000	1.22
+++ linux-2.5/arch/ppc/mm/fault.c	25 Sep 2004 01:46:10 -0000
@@ -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:
Index: linux-2.5/arch/ppc64/mm/fault.c
===================================================================
RCS file: /home/andrea/crypto/cvs/linux-2.5/arch/ppc64/mm/fault.c,v
retrieving revision 1.20
diff -u -p -r1.20 fault.c
--- linux-2.5/arch/ppc64/mm/fault.c	2 Aug 2004 17:11:09 -0000	1.20
+++ linux-2.5/arch/ppc64/mm/fault.c	25 Sep 2004 01:46:10 -0000
@@ -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:
Index: linux-2.5/arch/ppc64/mm/hugetlbpage.c
===================================================================
RCS file: /home/andrea/crypto/cvs/linux-2.5/arch/ppc64/mm/hugetlbpage.c,v
retrieving revision 1.32
diff -u -p -r1.32 hugetlbpage.c
--- linux-2.5/arch/ppc64/mm/hugetlbpage.c	17 Sep 2004 18:59:04 -0000	1.32
+++ linux-2.5/arch/ppc64/mm/hugetlbpage.c	12 Oct 2004 00:05:21 -0000
@@ -496,6 +496,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)) {
@@ -508,7 +509,15 @@ 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 = get_current_heap_stack_gap();
+		/* must check for overflow too */
+		if (addr + len + __heap_stack_gap <= vma->vm_start &&
+		    likely(addr + len + __heap_stack_gap >= addr + len)) {
+		got_it:
 			/*
 			 * Remember the place where we stopped the search:
 			 */
Index: linux-2.5/arch/s390/mm/fault.c
===================================================================
RCS file: /home/andrea/crypto/cvs/linux-2.5/arch/s390/mm/fault.c,v
retrieving revision 1.20
diff -u -p -r1.20 fault.c
--- linux-2.5/arch/s390/mm/fault.c	8 Sep 2004 14:48:45 -0000	1.20
+++ linux-2.5/arch/s390/mm/fault.c	25 Sep 2004 01:46:10 -0000
@@ -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
Index: linux-2.5/arch/sh/mm/fault.c
===================================================================
RCS file: /home/andrea/crypto/cvs/linux-2.5/arch/sh/mm/fault.c,v
retrieving revision 1.14
diff -u -p -r1.14 fault.c
--- linux-2.5/arch/sh/mm/fault.c	8 Sep 2004 14:48:45 -0000	1.14
+++ linux-2.5/arch/sh/mm/fault.c	25 Sep 2004 01:46:10 -0000
@@ -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
Index: linux-2.5/arch/sh64/mm/fault.c
===================================================================
RCS file: /home/andrea/crypto/cvs/linux-2.5/arch/sh64/mm/fault.c,v
retrieving revision 1.3
diff -u -p -r1.3 fault.c
--- linux-2.5/arch/sh64/mm/fault.c	8 Sep 2004 14:48:45 -0000	1.3
+++ linux-2.5/arch/sh64/mm/fault.c	25 Sep 2004 01:46:10 -0000
@@ -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",
Index: linux-2.5/arch/sparc/mm/fault.c
===================================================================
RCS file: /home/andrea/crypto/cvs/linux-2.5/arch/sparc/mm/fault.c,v
retrieving revision 1.19
diff -u -p -r1.19 fault.c
--- linux-2.5/arch/sparc/mm/fault.c	13 Jul 2004 18:02:33 -0000	1.19
+++ linux-2.5/arch/sparc/mm/fault.c	25 Sep 2004 01:46:10 -0000
@@ -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;
Index: linux-2.5/arch/sparc64/mm/fault.c
===================================================================
RCS file: /home/andrea/crypto/cvs/linux-2.5/arch/sparc64/mm/fault.c,v
retrieving revision 1.23
diff -u -p -r1.23 fault.c
--- linux-2.5/arch/sparc64/mm/fault.c	3 Oct 2004 16:14:25 -0000	1.23
+++ linux-2.5/arch/sparc64/mm/fault.c	4 Oct 2004 17:59:11 -0000
@@ -409,7 +409,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
Index: linux-2.5/arch/um/kernel/trap_kern.c
===================================================================
RCS file: /home/andrea/crypto/cvs/linux-2.5/arch/um/kernel/trap_kern.c,v
retrieving revision 1.10
diff -u -p -r1.10 trap_kern.c
--- linux-2.5/arch/um/kernel/trap_kern.c	24 Aug 2004 18:18:53 -0000	1.10
+++ linux-2.5/arch/um/kernel/trap_kern.c	25 Sep 2004 01:46:10 -0000
@@ -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;
Index: linux-2.5/arch/x86_64/kernel/sys_x86_64.c
===================================================================
RCS file: /home/andrea/crypto/cvs/linux-2.5/arch/x86_64/kernel/sys_x86_64.c,v
retrieving revision 1.18
diff -u -p -r1.18 sys_x86_64.c
--- linux-2.5/arch/x86_64/kernel/sys_x86_64.c	31 May 2004 03:07:42 -0000	1.18
+++ linux-2.5/arch/x86_64/kernel/sys_x86_64.c	12 Oct 2004 00:05:30 -0000
@@ -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,15 @@ 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 = get_current_heap_stack_gap();
+		/* check for overflow too */
+		if (addr + len + __heap_stack_gap <= vma->vm_start &&
+		    likely(addr + len + __heap_stack_gap >= addr + len)) {
+		got_it:
 			/*
 			 * Remember the place where we stopped the search:
 			 */
Index: linux-2.5/arch/x86_64/mm/fault.c
===================================================================
RCS file: /home/andrea/crypto/cvs/linux-2.5/arch/x86_64/mm/fault.c,v
retrieving revision 1.29
diff -u -p -r1.29 fault.c
--- linux-2.5/arch/x86_64/mm/fault.c	17 Sep 2004 19:00:12 -0000	1.29
+++ linux-2.5/arch/x86_64/mm/fault.c	25 Sep 2004 01:46:10 -0000
@@ -248,7 +248,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;
@@ -349,7 +349,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
Index: linux-2.5/include/linux/init_task.h
===================================================================
RCS file: /home/andrea/crypto/cvs/linux-2.5/include/linux/init_task.h,v
retrieving revision 1.32
diff -u -p -r1.32 init_task.h
--- linux-2.5/include/linux/init_task.h	30 Jun 2004 22:52:08 -0000	1.32
+++ linux-2.5/include/linux/init_task.h	12 Oct 2004 00:42:42 -0000
@@ -97,6 +97,7 @@ extern struct group_info init_groups;
 	.cap_permitted	= CAP_FULL_SET,					\
 	.keep_capabilities = 0,						\
 	.rlim		= INIT_RLIMITS,					\
+	.heap_stack_gap = -1UL,						\
 	.user		= INIT_USER,					\
 	.comm		= "swapper",					\
 	.thread		= INIT_THREAD,					\
Index: linux-2.5/include/linux/mm.h
===================================================================
RCS file: /home/andrea/crypto/cvs/linux-2.5/include/linux/mm.h,v
retrieving revision 1.190
diff -u -p -r1.190 mm.h
--- linux-2.5/include/linux/mm.h	3 Sep 2004 17:20:35 -0000	1.190
+++ linux-2.5/include/linux/mm.h	11 Oct 2004 23:46:06 -0000
@@ -730,7 +730,11 @@ 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 set_current_heap_stack_gap(unsigned long gap);
+extern unsigned long get_current_heap_stack_gap(void);
+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);
Index: linux-2.5/include/linux/prctl.h
===================================================================
RCS file: /home/andrea/crypto/cvs/linux-2.5/include/linux/prctl.h,v
retrieving revision 1.8
diff -u -p -r1.8 prctl.h
--- linux-2.5/include/linux/prctl.h	14 Sep 2004 14:48:01 -0000	1.8
+++ linux-2.5/include/linux/prctl.h	11 Oct 2004 23:44:30 -0000
@@ -52,4 +52,7 @@
 
 #define PR_SET_NAME    15		/* Set process name */
 
+#define PR_GET_HEAP_STACK_GAP	16	/* get current heap stack gap */
+#define PR_SET_HEAP_STACK_GAP	17	/* set current heap stack gap */
+
 #endif /* _LINUX_PRCTL_H */
Index: linux-2.5/include/linux/sched.h
===================================================================
RCS file: /home/andrea/crypto/cvs/linux-2.5/include/linux/sched.h,v
retrieving revision 1.273
diff -u -p -r1.273 sched.h
--- linux-2.5/include/linux/sched.h	5 Oct 2004 23:44:44 -0000	1.273
+++ linux-2.5/include/linux/sched.h	11 Oct 2004 23:51:47 -0000
@@ -520,6 +520,7 @@ struct task_struct {
 	struct user_struct *user;
 /* limits */
 	struct rlimit rlim[RLIM_NLIMITS];
+	unsigned long heap_stack_gap; /* used right before the rlimit */
 	unsigned short used_math;
 	char comm[16];
 /* file system info */
Index: linux-2.5/include/linux/sysctl.h
===================================================================
RCS file: /home/andrea/crypto/cvs/linux-2.5/include/linux/sysctl.h,v
retrieving revision 1.81
diff -u -p -r1.81 sysctl.h
--- linux-2.5/include/linux/sysctl.h	1 Oct 2004 22:57:47 -0000	1.81
+++ linux-2.5/include/linux/sysctl.h	4 Oct 2004 18:00:01 -0000
@@ -167,6 +167,7 @@ enum
 	VM_HUGETLB_GROUP=25,	/* permitted hugetlb group */
 	VM_VFS_CACHE_PRESSURE=26, /* dcache/icache reclaim pressure */
 	VM_LEGACY_VA_LAYOUT=27, /* legacy/compatibility virtual address space layout */
+ 	VM_HEAP_STACK_GAP=28,	/* int: page gap between heap and stack */
 };
 
 
Index: linux-2.5/kernel/sys.c
===================================================================
RCS file: /home/andrea/crypto/cvs/linux-2.5/kernel/sys.c,v
retrieving revision 1.93
diff -u -p -r1.93 sys.c
--- linux-2.5/kernel/sys.c	28 Sep 2004 16:06:30 -0000	1.93
+++ linux-2.5/kernel/sys.c	11 Oct 2004 23:45:39 -0000
@@ -1740,6 +1740,17 @@ asmlinkage long sys_prctl(int option, un
 			set_task_comm(me, ncomm);
 			return 0;
 		}
+		case PR_GET_HEAP_STACK_GAP:
+			/* return in bytes, cannot fail */
+			error = get_current_heap_stack_gap();
+			break;
+		case PR_SET_HEAP_STACK_GAP:
+			/*
+			 * arg2 is in byte unit unlike the heap-stack-gap sysctl
+			 * which is in PAGE_SIZE units.
+			 */
+			error = set_current_heap_stack_gap(arg2);
+			break;
 		default:
 			error = -EINVAL;
 			break;
Index: linux-2.5/kernel/sysctl.c
===================================================================
RCS file: /home/andrea/crypto/cvs/linux-2.5/kernel/sysctl.c,v
retrieving revision 1.89
diff -u -p -r1.89 sysctl.c
--- linux-2.5/kernel/sysctl.c	24 Aug 2004 19:40:58 -0000	1.89
+++ linux-2.5/kernel/sysctl.c	25 Sep 2004 01:46:10 -0000
@@ -800,6 +800,14 @@ static ctl_table vm_table[] = {
 		.extra1		= &zero,
 	},
 #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 }
 };
 
Index: linux-2.5/mm/mmap.c
===================================================================
RCS file: /home/andrea/crypto/cvs/linux-2.5/mm/mmap.c,v
retrieving revision 1.147
diff -u -p -r1.147 mmap.c
--- linux-2.5/mm/mmap.c	7 Oct 2004 01:16:18 -0000	1.147
+++ linux-2.5/mm/mmap.c	12 Oct 2004 00:45:00 -0000
@@ -58,12 +58,45 @@ 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);
 EXPORT_SYMBOL(sysctl_max_map_count);
 EXPORT_SYMBOL(vm_committed_space);
 
+int set_current_heap_stack_gap(unsigned long gap)
+{
+	/*
+	 * Let's be strict and verbose, so the app doesn't risk to
+	 * break in an opaque way when recompiled and run on a different
+	 * architecture. When no error is returned it means the raw value
+	 * was written in the task structure successfully.
+	 */
+	if (unlikely(gap & ~PAGE_MASK))
+		return -EINVAL;
+	/*
+	 * must be below TASK_SIZE or nothing could be mapped,
+	 * this is only for robustness, it's not required for
+	 * security, the overflow-check is the only thing
+	 * required for security. So we'll just be a bit less
+	 * strict on x86 emulation mode on x86-64, than on
+	 * a native x86 kernel, but nothing will go wrong.
+	 */
+	if (gap >= TASK_SIZE)
+		return -EINVAL;
+	current->heap_stack_gap = gap;
+	return 0;
+}
+
+unsigned long get_current_heap_stack_gap(void)
+{
+	if (current->heap_stack_gap != -1UL)
+		return current->heap_stack_gap;
+	else
+		return heap_stack_gap << PAGE_SHIFT;
+}
+
 /*
  * Requires inode->i_mapping->i_mmap_lock
  */
@@ -1071,6 +1104,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
@@ -1082,7 +1116,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 = get_current_heap_stack_gap();
+		if (addr + len + __heap_stack_gap <= vma->vm_start &&
+		    likely(addr + len + __heap_stack_gap >= addr + len)) {
+		got_it:
 			/*
 			 * Remember the place where we stopped the search:
 			 */
@@ -1317,13 +1358,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;
 
@@ -1375,7 +1420,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);
@@ -1386,9 +1431,10 @@ 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;
+	unsigned long grow, __heap_stack_gap;
 
 	/*
 	 * We must make sure the anon_vma is allocated
@@ -1404,10 +1450,16 @@ int expand_stack(struct vm_area_struct *
 	 * anon_vma lock to serialize against concurrent expand_stacks.
 	 */
 	address &= PAGE_MASK;
+	/* must check for overflow too */
+	__heap_stack_gap = get_current_heap_stack_gap();
+	if (prev_vma && unlikely(prev_vma->vm_end + __heap_stack_gap > address ||
+				 prev_vma->vm_end + __heap_stack_gap < prev_vma->vm_end))
+		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;
 	}
@@ -1432,7 +1484,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;
@@ -1444,7 +1496,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);