diff options
Diffstat (limited to 'mm/memory.c')
| -rw-r--r-- | mm/memory.c | 28 | 
1 files changed, 16 insertions, 12 deletions
diff --git a/mm/memory.c b/mm/memory.c index 01f39e8144ef..603b2f419948 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -5393,27 +5393,28 @@ retry:  	if (!vma_is_anonymous(vma) && !vma_is_tcp(vma))  		goto inval; -	/* find_mergeable_anon_vma uses adjacent vmas which are not locked */ -	if (!vma->anon_vma && !vma_is_tcp(vma)) -		goto inval; -  	if (!vma_start_read(vma))  		goto inval;  	/* +	 * find_mergeable_anon_vma uses adjacent vmas which are not locked. +	 * This check must happen after vma_start_read(); otherwise, a +	 * concurrent mremap() with MREMAP_DONTUNMAP could dissociate the VMA +	 * from its anon_vma. +	 */ +	if (unlikely(!vma->anon_vma && !vma_is_tcp(vma))) +		goto inval_end_read; + +	/*  	 * Due to the possibility of userfault handler dropping mmap_lock, avoid  	 * it for now and fall back to page fault handling under mmap_lock.  	 */ -	if (userfaultfd_armed(vma)) { -		vma_end_read(vma); -		goto inval; -	} +	if (userfaultfd_armed(vma)) +		goto inval_end_read;  	/* Check since vm_start/vm_end might change before we lock the VMA */ -	if (unlikely(address < vma->vm_start || address >= vma->vm_end)) { -		vma_end_read(vma); -		goto inval; -	} +	if (unlikely(address < vma->vm_start || address >= vma->vm_end)) +		goto inval_end_read;  	/* Check if the VMA got isolated after we found it */  	if (vma->detached) { @@ -5425,6 +5426,9 @@ retry:  	rcu_read_unlock();  	return vma; + +inval_end_read: +	vma_end_read(vma);  inval:  	rcu_read_unlock();  	count_vm_vma_lock_event(VMA_LOCK_ABORT);  |