heap-stack-gap for 2.6

From: Andrea Arcangeli
Date: Sat Sep 25 2004 - 11:25:22 EST


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. Against CVS head.

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.

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.40
diff -u -p -r1.40 fault.c
--- linux-2.5/arch/i386/mm/fault.c 8 Sep 2004 14:48:45 -0000 1.40
+++ linux-2.5/arch/i386/mm/fault.c 25 Sep 2004 01:46:10 -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.20
diff -u -p -r1.20 fault.c
--- linux-2.5/arch/ia64/mm/fault.c 8 Sep 2004 14:48:45 -0000 1.20
+++ linux-2.5/arch/ia64/mm/fault.c 25 Sep 2004 01:46:10 -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 25 Sep 2004 02:41:06 -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,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:
*/
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.22
diff -u -p -r1.22 fault.c
--- linux-2.5/arch/sparc64/mm/fault.c 2 Sep 2004 08:19:11 -0000 1.22
+++ linux-2.5/arch/sparc64/mm/fault.c 25 Sep 2004 01:46:10 -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 25 Sep 2004 02:40:56 -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,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:
*/
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/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 25 Sep 2004 02:32:38 -0000
@@ -730,7 +730,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);
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.79
diff -u -p -r1.79 sysctl.h
--- linux-2.5/include/linux/sysctl.h 24 Aug 2004 18:12:13 -0000 1.79
+++ linux-2.5/include/linux/sysctl.h 25 Sep 2004 01:46:10 -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/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.145
diff -u -p -r1.145 mmap.c
--- linux-2.5/mm/mmap.c 3 Sep 2004 17:22:55 -0000 1.145
+++ linux-2.5/mm/mmap.c 25 Sep 2004 01:46:10 -0000
@@ -58,6 +58,7 @@ int sysctl_overcommit_memory = 0; /* def
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);
@@ -1069,6 +1070,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
@@ -1080,7 +1082,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:
*/
@@ -1315,13 +1323,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;

@@ -1373,7 +1385,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);
@@ -1384,7 +1396,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;

@@ -1402,10 +1415,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;
}
@@ -1430,7 +1446,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;
@@ -1442,7 +1458,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);
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/