diff -urNp --exclude CVS --exclude BitKeeper --exclude {arch} --exclude .arch-ids x-ref/arch/alpha/mm/fault.c 2.4.23pre7aa1/arch/alpha/mm/fault.c
--- x-ref/arch/alpha/mm/fault.c	2002-11-29 02:22:55.000000000 +0100
+++ 2.4.23pre7aa1/arch/alpha/mm/fault.c	2003-10-17 21:13:22.000000000 +0200
@@ -124,7 +124,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 -urNp --exclude CVS --exclude BitKeeper --exclude {arch} --exclude .arch-ids x-ref/arch/arm/mm/fault-common.c 2.4.23pre7aa1/arch/arm/mm/fault-common.c
--- x-ref/arch/arm/mm/fault-common.c	2003-08-26 00:12:43.000000000 +0200
+++ 2.4.23pre7aa1/arch/arm/mm/fault-common.c	2003-10-17 21:13:22.000000000 +0200
@@ -254,7 +254,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 -urNp --exclude CVS --exclude BitKeeper --exclude {arch} --exclude .arch-ids x-ref/arch/cris/mm/fault.c 2.4.23pre7aa1/arch/cris/mm/fault.c
--- x-ref/arch/cris/mm/fault.c	2003-08-26 00:12:43.000000000 +0200
+++ 2.4.23pre7aa1/arch/cris/mm/fault.c	2003-10-17 21:13:22.000000000 +0200
@@ -323,7 +323,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 -urNp --exclude CVS --exclude BitKeeper --exclude {arch} --exclude .arch-ids x-ref/arch/i386/mm/fault.c 2.4.23pre7aa1/arch/i386/mm/fault.c
--- x-ref/arch/i386/mm/fault.c	2003-10-17 21:13:18.000000000 +0200
+++ 2.4.23pre7aa1/arch/i386/mm/fault.c	2003-10-17 21:13:22.000000000 +0200
@@ -34,7 +34,7 @@ spinlock_t oops_lock = SPIN_LOCK_UNLOCKE
  */
 int __verify_write(const void * addr, unsigned long size)
 {
-	struct vm_area_struct * vma;
+	struct vm_area_struct * vma, * prev_vma;
 	unsigned long start = (unsigned long) addr;
 
 	if (!size)
@@ -80,7 +80,8 @@ good_area:
 check_stack:
 	if (!(vma->vm_flags & VM_GROWSDOWN))
 		goto bad_area;
-	if (expand_stack(vma, start) == 0)
+	find_vma_prev(current->mm, start, &prev_vma);
+	if (expand_stack(vma, start, prev_vma) == 0)
 		goto good_area;
 
 bad_area:
@@ -143,7 +144,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;
 	unsigned long fixup;
@@ -204,7 +205,8 @@ asmlinkage void do_page_fault(struct pt_
 		if (address + 32 < regs->esp)
 			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;
 /*
  * Ok, we have a good vm_area for this memory access, so
diff -urNp --exclude CVS --exclude BitKeeper --exclude {arch} --exclude .arch-ids x-ref/arch/ia64/mm/fault.c 2.4.23pre7aa1/arch/ia64/mm/fault.c
--- x-ref/arch/ia64/mm/fault.c	2003-08-26 00:12:44.000000000 +0200
+++ 2.4.23pre7aa1/arch/ia64/mm/fault.c	2003-10-17 21:13:22.000000000 +0200
@@ -159,7 +159,7 @@ ia64_do_page_fault (unsigned long addres
 		if (rgn_index(address) != rgn_index(vma->vm_start)
 		    || rgn_offset(address) >= RGN_MAP_LIMIT)
 			goto bad_area;
-		if (expand_stack(vma, address))
+		if (expand_stack(vma, address, NULL))
 			goto bad_area;
 	} else {
 		vma = prev_vma;
diff -urNp --exclude CVS --exclude BitKeeper --exclude {arch} --exclude .arch-ids x-ref/arch/m68k/mm/fault.c 2.4.23pre7aa1/arch/m68k/mm/fault.c
--- x-ref/arch/m68k/mm/fault.c	2002-11-29 02:22:55.000000000 +0100
+++ 2.4.23pre7aa1/arch/m68k/mm/fault.c	2003-10-17 21:13:22.000000000 +0200
@@ -120,7 +120,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 -urNp --exclude CVS --exclude BitKeeper --exclude {arch} --exclude .arch-ids x-ref/arch/mips/mm/fault.c 2.4.23pre7aa1/arch/mips/mm/fault.c
--- x-ref/arch/mips/mm/fault.c	2003-08-26 00:12:46.000000000 +0200
+++ 2.4.23pre7aa1/arch/mips/mm/fault.c	2003-10-17 21:13:22.000000000 +0200
@@ -114,7 +114,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 -urNp --exclude CVS --exclude BitKeeper --exclude {arch} --exclude .arch-ids x-ref/arch/mips64/mm/fault.c 2.4.23pre7aa1/arch/mips64/mm/fault.c
--- x-ref/arch/mips64/mm/fault.c	2003-08-26 00:12:46.000000000 +0200
+++ 2.4.23pre7aa1/arch/mips64/mm/fault.c	2003-10-17 21:13:22.000000000 +0200
@@ -137,7 +137,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 -urNp --exclude CVS --exclude BitKeeper --exclude {arch} --exclude .arch-ids x-ref/arch/ppc/mm/fault.c 2.4.23pre7aa1/arch/ppc/mm/fault.c
--- x-ref/arch/ppc/mm/fault.c	2003-10-10 08:07:22.000000000 +0200
+++ 2.4.23pre7aa1/arch/ppc/mm/fault.c	2003-10-17 21:13:22.000000000 +0200
@@ -96,7 +96,7 @@ static int store_updates_sp(struct pt_re
 void 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;
@@ -177,7 +177,8 @@ void do_page_fault(struct pt_regs *regs,
 		    && (!user_mode(regs) || !store_updates_sp(regs)))
 			goto bad_area;
 	}
-	if (expand_stack(vma, address))
+	vma = find_vma_prev(mm, address, &prev_vma);
+	if (expand_stack(vma, address, prev_vma))
 		goto bad_area;
 
 good_area:
diff -urNp --exclude CVS --exclude BitKeeper --exclude {arch} --exclude .arch-ids x-ref/arch/s390/mm/fault.c 2.4.23pre7aa1/arch/s390/mm/fault.c
--- x-ref/arch/s390/mm/fault.c	2002-11-29 02:22:57.000000000 +0100
+++ 2.4.23pre7aa1/arch/s390/mm/fault.c	2003-10-17 21:13:22.000000000 +0200
@@ -210,7 +210,7 @@ extern inline void do_exception(struct p
                 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 -urNp --exclude CVS --exclude BitKeeper --exclude {arch} --exclude .arch-ids x-ref/arch/s390x/mm/fault.c 2.4.23pre7aa1/arch/s390x/mm/fault.c
--- x-ref/arch/s390x/mm/fault.c	2002-11-29 02:22:58.000000000 +0100
+++ 2.4.23pre7aa1/arch/s390x/mm/fault.c	2003-10-17 21:13:22.000000000 +0200
@@ -210,7 +210,7 @@ extern inline void do_exception(struct p
                 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 -urNp --exclude CVS --exclude BitKeeper --exclude {arch} --exclude .arch-ids x-ref/arch/sh/mm/fault.c 2.4.23pre7aa1/arch/sh/mm/fault.c
--- x-ref/arch/sh/mm/fault.c	2003-08-26 00:12:51.000000000 +0200
+++ 2.4.23pre7aa1/arch/sh/mm/fault.c	2003-10-17 21:13:22.000000000 +0200
@@ -78,7 +78,7 @@ good_area:
 check_stack:
 	if (!(vma->vm_flags & VM_GROWSDOWN))
 		goto bad_area;
-	if (expand_stack(vma, start) == 0)
+	if (expand_stack(vma, start, NULL) == 0)
 		goto good_area;
 
 bad_area:
@@ -123,7 +123,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 -urNp --exclude CVS --exclude BitKeeper --exclude {arch} --exclude .arch-ids x-ref/arch/sparc/mm/fault.c 2.4.23pre7aa1/arch/sparc/mm/fault.c
--- x-ref/arch/sparc/mm/fault.c	2003-06-13 22:07:24.000000000 +0200
+++ 2.4.23pre7aa1/arch/sparc/mm/fault.c	2003-10-17 21:13:22.000000000 +0200
@@ -251,7 +251,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
@@ -498,7 +498,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 -urNp --exclude CVS --exclude BitKeeper --exclude {arch} --exclude .arch-ids x-ref/arch/sparc64/mm/fault.c 2.4.23pre7aa1/arch/sparc64/mm/fault.c
--- x-ref/arch/sparc64/mm/fault.c	2002-11-29 02:22:58.000000000 +0100
+++ 2.4.23pre7aa1/arch/sparc64/mm/fault.c	2003-10-17 21:13:22.000000000 +0200
@@ -389,7 +389,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 -urNp --exclude CVS --exclude BitKeeper --exclude {arch} --exclude .arch-ids x-ref/include/linux/mm.h 2.4.23pre7aa1/include/linux/mm.h
--- x-ref/include/linux/mm.h	2003-10-17 21:13:19.000000000 +0200
+++ 2.4.23pre7aa1/include/linux/mm.h	2003-10-17 21:13:22.000000000 +0200
@@ -639,12 +639,26 @@ static inline unsigned int pf_gfp_mask(u
 
 	return gfp_mask;
 }
-	
-/* vma is the first one with  address < vma->vm_end,
- * and even  address < vma->vm_start. Have to extend vma. */
-static inline int expand_stack(struct vm_area_struct * vma, unsigned long address)
+
+extern int heap_stack_gap;
+
+/*
+ * vma is the first one with  address < vma->vm_end,
+ * and even  address < vma->vm_start. Have to extend vma.
+ *
+ * Locking: vm_start can decrease under you if you only hold
+ * the read semaphore, you either need the write semaphore
+ * or both the read semaphore and the page_table_lock acquired
+ * if you want vm_start consistent. vm_end and the vma layout
+ * are just consistent with only the read semaphore acquired
+ * instead.
+ */
+#define EXPAND_STACK_HAS_3_ARGS
+static inline int expand_stack(struct vm_area_struct * vma, unsigned long address,
+			       struct vm_area_struct * prev_vma)
 {
 	unsigned long grow;
+	int err = -ENOMEM;
 
 	/*
 	 * vma->vm_start/vm_end cannot change under us because the caller is required
@@ -652,20 +666,23 @@ static inline int expand_stack(struct vm
 	 * before relocating the vma range ourself.
 	 */
 	address &= PAGE_MASK;
- 	spin_lock(&vma->vm_mm->page_table_lock);
+	if (prev_vma && prev_vma->vm_end + (heap_stack_gap << PAGE_SHIFT) > address)
+		goto out;
+	spin_lock(&vma->vm_mm->page_table_lock);
 	grow = (vma->vm_start - address) >> PAGE_SHIFT;
 	if (vma->vm_end - address > current->rlim[RLIMIT_STACK].rlim_cur ||
-	    ((vma->vm_mm->total_vm + grow) << PAGE_SHIFT) > current->rlim[RLIMIT_AS].rlim_cur) {
-		spin_unlock(&vma->vm_mm->page_table_lock);
-		return -ENOMEM;
-	}
+	    ((vma->vm_mm->total_vm + grow) << PAGE_SHIFT) > current->rlim[RLIMIT_AS].rlim_cur)
+		goto out_unlock;
 	vma->vm_start = address;
 	vma->vm_pgoff -= grow;
 	vma->vm_mm->total_vm += grow;
 	if (vma->vm_flags & VM_LOCKED)
 		vma->vm_mm->locked_vm += grow;
+	err = 0;
+ out_unlock:
 	spin_unlock(&vma->vm_mm->page_table_lock);
-	return 0;
+ out:
+	return err;
 }
 
 /* Look up the first VMA which satisfies  addr < vm_end,  NULL if none. */
diff -urNp --exclude CVS --exclude BitKeeper --exclude {arch} --exclude .arch-ids x-ref/include/linux/sysctl.h 2.4.23pre7aa1/include/linux/sysctl.h
--- x-ref/include/linux/sysctl.h	2003-10-10 08:08:29.000000000 +0200
+++ 2.4.23pre7aa1/include/linux/sysctl.h	2003-10-17 21:14:07.000000000 +0200
@@ -156,6 +156,7 @@ enum
 	VM_MAPPED_RATIO=20,     /* amount of unfreeable pages that triggers swapout */
 	VM_LAPTOP_MODE=21,	/* kernel in laptop flush mode */
 	VM_BLOCK_DUMP=22,	/* dump fs activity to log */
+ 	VM_HEAP_STACK_GAP=23,	/* int: page gap between heap and stack */
 };
 
 
diff -urNp --exclude CVS --exclude BitKeeper --exclude {arch} --exclude .arch-ids x-ref/kernel/sysctl.c 2.4.23pre7aa1/kernel/sysctl.c
--- x-ref/kernel/sysctl.c	2003-10-10 08:08:32.000000000 +0200
+++ 2.4.23pre7aa1/kernel/sysctl.c	2003-10-17 21:13:22.000000000 +0200
@@ -302,6 +302,8 @@ static ctl_table vm_table[] = {
 	 &pgt_cache_water, 2*sizeof(int), 0644, NULL, &proc_dointvec},
 	{VM_PAGE_CLUSTER, "page-cluster", 
 	 &page_cluster, sizeof(int), 0644, NULL, &proc_dointvec},
+	{VM_HEAP_STACK_GAP, "heap-stack-gap", 
+	 &heap_stack_gap, sizeof(int), 0644, NULL, &proc_dointvec},
 	{VM_MIN_READAHEAD, "min-readahead",
 	&vm_min_readahead,sizeof(int), 0644, NULL, &proc_dointvec},
 	{VM_MAX_READAHEAD, "max-readahead",
diff -urNp --exclude CVS --exclude BitKeeper --exclude {arch} --exclude .arch-ids x-ref/mm/mmap.c 2.4.23pre7aa1/mm/mmap.c
--- x-ref/mm/mmap.c	2003-10-17 21:13:18.000000000 +0200
+++ 2.4.23pre7aa1/mm/mmap.c	2003-10-17 21:13:22.000000000 +0200
@@ -45,6 +45,7 @@ pgprot_t protection_map[16] = {
 };
 
 int sysctl_overcommit_memory;
+int heap_stack_gap = 1;
 int max_map_count = DEFAULT_MAX_MAP_COUNT;
 
 /* Check that a process has enough memory to allocate a
@@ -635,9 +636,15 @@ static inline unsigned long arch_get_unm
 
 	for (vma = find_vma(current->mm, addr); ; vma = vma->vm_next) {
 		/* At this point:  (!vma || addr < vma->vm_end). */
+		unsigned long __heap_stack_gap;
 		if (TASK_SIZE - len < addr)
 			return -ENOMEM;
-		if (!vma || addr + len <= vma->vm_start)
+		if (!vma)
+			return addr;
+		__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)
 			return addr;
 		addr = vma->vm_end;
 	}
@@ -746,7 +753,7 @@ struct vm_area_struct * find_vma_prev(st
 
 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;
@@ -758,7 +765,8 @@ struct vm_area_struct * find_extend_vma(
 	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);