diff options
Diffstat (limited to 'mm/memory.c')
| -rw-r--r-- | mm/memory.c | 30 | 
1 files changed, 22 insertions, 8 deletions
diff --git a/mm/memory.c b/mm/memory.c index b6e5fd23cc5a..6b2ab1051851 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -2760,21 +2760,35 @@ out_release:  }  /* - * This is like a special single-page "expand_downwards()", - * except we must first make sure that 'address-PAGE_SIZE' + * This is like a special single-page "expand_{down|up}wards()", + * except we must first make sure that 'address{-|+}PAGE_SIZE'   * doesn't hit another vma. - * - * The "find_vma()" will do the right thing even if we wrap   */  static inline int check_stack_guard_page(struct vm_area_struct *vma, unsigned long address)  {  	address &= PAGE_MASK;  	if ((vma->vm_flags & VM_GROWSDOWN) && address == vma->vm_start) { -		address -= PAGE_SIZE; -		if (find_vma(vma->vm_mm, address) != vma) -			return -ENOMEM; +		struct vm_area_struct *prev = vma->vm_prev; + +		/* +		 * Is there a mapping abutting this one below? +		 * +		 * That's only ok if it's the same stack mapping +		 * that has gotten split.. +		 */ +		if (prev && prev->vm_end == address) +			return prev->vm_flags & VM_GROWSDOWN ? 0 : -ENOMEM; + +		expand_stack(vma, address - PAGE_SIZE); +	} +	if ((vma->vm_flags & VM_GROWSUP) && address + PAGE_SIZE == vma->vm_end) { +		struct vm_area_struct *next = vma->vm_next; + +		/* As VM_GROWSDOWN but s/below/above/ */ +		if (next && next->vm_start == address + PAGE_SIZE) +			return next->vm_flags & VM_GROWSUP ? 0 : -ENOMEM; -		expand_stack(vma, address); +		expand_upwards(vma, address + PAGE_SIZE);  	}  	return 0;  }  | 
