Signed-Off-By: Andrea Arcangeli 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);