From: Andrea Arcangeli 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 --- 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/m32r/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 +++++++++++++++++++++++--------- 25 files changed, 97 insertions(+), 38 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-12-03 20:57:05.877745248 -0800 +++ 25-akpm/arch/alpha/mm/fault.c 2004-12-03 20:57:05.913739776 -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-12-03 20:57:05.878745096 -0800 +++ 25-akpm/arch/arm26/mm/fault.c 2004-12-03 20:57:05.914739624 -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-12-03 20:57:05.880744792 -0800 +++ 25-akpm/arch/arm/mm/fault.c 2004-12-03 20:57:05.914739624 -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-12-03 20:57:05.881744640 -0800 +++ 25-akpm/arch/cris/mm/fault.c 2004-12-03 20:57:05.915739472 -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-12-03 20:57:05.882744488 -0800 +++ 25-akpm/arch/i386/mm/fault.c 2004-12-03 20:57:05.915739472 -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-12-03 20:57:05.884744184 -0800 +++ 25-akpm/arch/ia64/mm/fault.c 2004-12-03 20:57:05.916739320 -0800 @@ -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; 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-12-03 20:57:05.885744032 -0800 +++ 25-akpm/arch/m68k/mm/fault.c 2004-12-03 20:57:05.916739320 -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-12-03 20:57:05.886743880 -0800 +++ 25-akpm/arch/mips/mm/fault.c 2004-12-03 20:57:05.917739168 -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-12-03 20:57:05.888743576 -0800 +++ 25-akpm/arch/parisc/mm/fault.c 2004-12-03 20:57:05.917739168 -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-12-03 20:57:05.889743424 -0800 +++ 25-akpm/arch/ppc64/mm/fault.c 2004-12-03 20:57:05.917739168 -0800 @@ -87,7 +87,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; @@ -193,7 +193,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-12-03 20:57:05.890743272 -0800 +++ 25-akpm/arch/ppc64/mm/hugetlbpage.c 2004-12-03 20:57:05.918739016 -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-12-03 20:57:05.892742968 -0800 +++ 25-akpm/arch/ppc/mm/fault.c 2004-12-03 20:57:05.919738864 -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-12-03 20:57:05.893742816 -0800 +++ 25-akpm/arch/s390/mm/fault.c 2004-12-03 20:57:05.920738712 -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-12-03 20:57:05.894742664 -0800 +++ 25-akpm/arch/sh64/mm/fault.c 2004-12-03 20:57:05.920738712 -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-12-03 20:57:05.896742360 -0800 +++ 25-akpm/arch/sh/mm/fault.c 2004-12-03 20:57:05.921738560 -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-12-03 20:57:05.897742208 -0800 +++ 25-akpm/arch/sparc64/mm/fault.c 2004-12-03 20:57:05.921738560 -0800 @@ -418,7 +418,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-12-03 20:57:05.899741904 -0800 +++ 25-akpm/arch/sparc/mm/fault.c 2004-12-03 20:57:05.922738408 -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-12-03 20:57:05.900741752 -0800 +++ 25-akpm/arch/um/kernel/trap_kern.c 2004-12-03 20:57:05.923738256 -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-12-03 20:57:05.901741600 -0800 +++ 25-akpm/arch/x86_64/kernel/sys_x86_64.c 2004-12-03 20:57:05.923738256 -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-12-03 20:57:05.902741448 -0800 +++ 25-akpm/arch/x86_64/mm/fault.c 2004-12-03 20:57:05.924738104 -0800 @@ -298,7 +298,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; @@ -405,7 +405,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-12-03 20:57:05.904741144 -0800 +++ 25-akpm/include/linux/mm.h 2004-12-03 20:57:05.925737952 -0800 @@ -803,7 +803,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-12-03 20:57:05.905740992 -0800 +++ 25-akpm/include/linux/sysctl.h 2004-12-03 20:57:05.926737800 -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-12-03 20:57:05.907740688 -0800 +++ 25-akpm/kernel/sysctl.c 2004-12-03 20:57:05.928737496 -0800 @@ -817,6 +817,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-12-03 20:57:05.908740536 -0800 +++ 25-akpm/mm/mmap.c 2004-12-03 20:57:05.930737192 -0800 @@ -60,6 +60,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); @@ -1090,6 +1091,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 @@ -1101,7 +1103,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: */ @@ -1336,13 +1344,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; @@ -1396,7 +1408,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); @@ -1407,7 +1419,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; @@ -1425,10 +1438,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; } @@ -1455,7 +1471,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; @@ -1467,7 +1483,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); diff -puN arch/m32r/mm/fault.c~enforce-a-gap-between-heap-and-stack arch/m32r/mm/fault.c --- 25/arch/m32r/mm/fault.c~enforce-a-gap-between-heap-and-stack 2004-12-03 20:57:05.910740232 -0800 +++ 25-akpm/arch/m32r/mm/fault.c 2004-12-03 20:57:05.931737040 -0800 @@ -182,7 +182,7 @@ asmlinkage void do_page_fault(struct pt_ goto bad_area; } #endif - 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 _