diff -urNp x-ref/arch/alpha/mm/fault.c x/arch/alpha/mm/fault.c --- x-ref/arch/alpha/mm/fault.c 2002-11-29 02:22:55.000000000 +0100 +++ x/arch/alpha/mm/fault.c 2003-03-11 20:09:39.000000000 +0100 @@ -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 x-ref/arch/arm/mm/fault-common.c x/arch/arm/mm/fault-common.c --- x-ref/arch/arm/mm/fault-common.c 2002-11-29 02:22:55.000000000 +0100 +++ x/arch/arm/mm/fault-common.c 2003-03-11 20:09:39.000000000 +0100 @@ -229,7 +229,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 x-ref/arch/cris/mm/fault.c x/arch/cris/mm/fault.c --- x-ref/arch/cris/mm/fault.c 2002-11-29 02:22:55.000000000 +0100 +++ x/arch/cris/mm/fault.c 2003-03-11 20:09:39.000000000 +0100 @@ -315,7 +315,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 x-ref/arch/i386/mm/fault.c x/arch/i386/mm/fault.c --- x-ref/arch/i386/mm/fault.c 2003-03-11 20:09:34.000000000 +0100 +++ x/arch/i386/mm/fault.c 2003-03-11 20:09:39.000000000 +0100 @@ -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 x-ref/arch/ia64/mm/fault.c x/arch/ia64/mm/fault.c --- x-ref/arch/ia64/mm/fault.c 2003-02-27 08:15:00.000000000 +0100 +++ x/arch/ia64/mm/fault.c 2003-03-11 20:09:39.000000000 +0100 @@ -132,7 +132,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 x-ref/arch/m68k/mm/fault.c x/arch/m68k/mm/fault.c --- x-ref/arch/m68k/mm/fault.c 2002-11-29 02:22:55.000000000 +0100 +++ x/arch/m68k/mm/fault.c 2003-03-11 20:09:39.000000000 +0100 @@ -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 x-ref/arch/mips/mm/fault.c x/arch/mips/mm/fault.c --- x-ref/arch/mips/mm/fault.c 2002-11-29 02:22:56.000000000 +0100 +++ x/arch/mips/mm/fault.c 2003-03-11 20:09:39.000000000 +0100 @@ -113,7 +113,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 x-ref/arch/mips64/mm/fault.c x/arch/mips64/mm/fault.c --- x-ref/arch/mips64/mm/fault.c 2002-11-29 02:22:56.000000000 +0100 +++ x/arch/mips64/mm/fault.c 2003-03-11 20:09:39.000000000 +0100 @@ -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 x-ref/arch/ppc/mm/fault.c x/arch/ppc/mm/fault.c --- x-ref/arch/ppc/mm/fault.c 2003-02-27 08:15:05.000000000 +0100 +++ x/arch/ppc/mm/fault.c 2003-03-11 20:10:00.000000000 +0100 @@ -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 x-ref/arch/s390/mm/fault.c x/arch/s390/mm/fault.c --- x-ref/arch/s390/mm/fault.c 2002-11-29 02:22:57.000000000 +0100 +++ x/arch/s390/mm/fault.c 2003-03-11 20:09:39.000000000 +0100 @@ -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 x-ref/arch/s390x/mm/fault.c x/arch/s390x/mm/fault.c --- x-ref/arch/s390x/mm/fault.c 2002-11-29 02:22:58.000000000 +0100 +++ x/arch/s390x/mm/fault.c 2003-03-11 20:09:39.000000000 +0100 @@ -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 x-ref/arch/sh/mm/fault.c x/arch/sh/mm/fault.c --- x-ref/arch/sh/mm/fault.c 2002-11-29 02:22:58.000000000 +0100 +++ x/arch/sh/mm/fault.c 2003-03-11 20:09:39.000000000 +0100 @@ -74,7 +74,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: @@ -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 x-ref/arch/sparc/mm/fault.c x/arch/sparc/mm/fault.c --- x-ref/arch/sparc/mm/fault.c 2002-01-22 18:56:12.000000000 +0100 +++ x/arch/sparc/mm/fault.c 2003-03-11 20:09:39.000000000 +0100 @@ -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 x-ref/arch/sparc64/mm/fault.c x/arch/sparc64/mm/fault.c --- x-ref/arch/sparc64/mm/fault.c 2002-11-29 02:22:58.000000000 +0100 +++ x/arch/sparc64/mm/fault.c 2003-03-11 20:09:39.000000000 +0100 @@ -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 x-ref/include/linux/mm.h x/include/linux/mm.h --- x-ref/include/linux/mm.h 2003-03-11 20:09:35.000000000 +0100 +++ x/include/linux/mm.h 2003-03-11 20:09:39.000000000 +0100 @@ -627,12 +627,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 @@ -640,20 +654,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 x-ref/include/linux/sysctl.h x/include/linux/sysctl.h --- x-ref/include/linux/sysctl.h 2003-02-27 08:15:42.000000000 +0100 +++ x/include/linux/sysctl.h 2003-03-11 20:09:39.000000000 +0100 @@ -144,6 +144,7 @@ enum VM_MAX_MAP_COUNT=11, /* int: Maximum number of active map areas */ VM_MIN_READAHEAD=12, /* Min file readahead */ VM_MAX_READAHEAD=13, /* Max file readahead */ + VM_HEAP_STACK_GAP=14, /* int: page gap between heap and stack */ }; diff -urNp x-ref/kernel/sysctl.c x/kernel/sysctl.c --- x-ref/kernel/sysctl.c 2003-02-27 08:15:43.000000000 +0100 +++ x/kernel/sysctl.c 2003-03-11 20:09:39.000000000 +0100 @@ -274,6 +274,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 x-ref/mm/mmap.c x/mm/mmap.c --- x-ref/mm/mmap.c 2003-03-11 20:09:34.000000000 +0100 +++ x/mm/mmap.c 2003-03-11 20:09:41.000000000 +0100 @@ -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);