diff options
Diffstat (limited to 'mm/mmap.c')
| -rw-r--r-- | mm/mmap.c | 17 | 
1 files changed, 14 insertions, 3 deletions
diff --git a/mm/mmap.c b/mm/mmap.c index 3f758c7f4c8..da15a79b144 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -1266,8 +1266,9 @@ munmap_back:  	vma->vm_pgoff = pgoff;  	INIT_LIST_HEAD(&vma->anon_vma_chain); +	error = -EINVAL;	/* when rejecting VM_GROWSDOWN|VM_GROWSUP */ +  	if (file) { -		error = -EINVAL;  		if (vm_flags & (VM_GROWSDOWN|VM_GROWSUP))  			goto free_vma;  		if (vm_flags & VM_DENYWRITE) { @@ -1293,6 +1294,8 @@ munmap_back:  		pgoff = vma->vm_pgoff;  		vm_flags = vma->vm_flags;  	} else if (vm_flags & VM_SHARED) { +		if (unlikely(vm_flags & (VM_GROWSDOWN|VM_GROWSUP))) +			goto free_vma;  		error = shmem_zero_setup(vma);  		if (error)  			goto free_vma; @@ -1605,7 +1608,6 @@ EXPORT_SYMBOL(find_vma);  /*   * Same as find_vma, but also return a pointer to the previous VMA in *pprev. - * Note: pprev is set to NULL when return value is NULL.   */  struct vm_area_struct *  find_vma_prev(struct mm_struct *mm, unsigned long addr, @@ -1614,7 +1616,16 @@ find_vma_prev(struct mm_struct *mm, unsigned long addr,  	struct vm_area_struct *vma;  	vma = find_vma(mm, addr); -	*pprev = vma ? vma->vm_prev : NULL; +	if (vma) { +		*pprev = vma->vm_prev; +	} else { +		struct rb_node *rb_node = mm->mm_rb.rb_node; +		*pprev = NULL; +		while (rb_node) { +			*pprev = rb_entry(rb_node, struct vm_area_struct, vm_rb); +			rb_node = rb_node->rb_right; +		} +	}  	return vma;  }  | 
