diff options
Diffstat (limited to 'fs/userfaultfd.c')
| -rw-r--r-- | fs/userfaultfd.c | 97 | 
1 files changed, 52 insertions, 45 deletions
| diff --git a/fs/userfaultfd.c b/fs/userfaultfd.c index 22f4bf956ba1..54c6cc7fe9c6 100644 --- a/fs/userfaultfd.c +++ b/fs/userfaultfd.c @@ -165,14 +165,14 @@ static void userfaultfd_ctx_get(struct userfaultfd_ctx *ctx)  static void userfaultfd_ctx_put(struct userfaultfd_ctx *ctx)  {  	if (refcount_dec_and_test(&ctx->refcount)) { -		VM_BUG_ON(spin_is_locked(&ctx->fault_pending_wqh.lock)); -		VM_BUG_ON(waitqueue_active(&ctx->fault_pending_wqh)); -		VM_BUG_ON(spin_is_locked(&ctx->fault_wqh.lock)); -		VM_BUG_ON(waitqueue_active(&ctx->fault_wqh)); -		VM_BUG_ON(spin_is_locked(&ctx->event_wqh.lock)); -		VM_BUG_ON(waitqueue_active(&ctx->event_wqh)); -		VM_BUG_ON(spin_is_locked(&ctx->fd_wqh.lock)); -		VM_BUG_ON(waitqueue_active(&ctx->fd_wqh)); +		VM_WARN_ON_ONCE(spin_is_locked(&ctx->fault_pending_wqh.lock)); +		VM_WARN_ON_ONCE(waitqueue_active(&ctx->fault_pending_wqh)); +		VM_WARN_ON_ONCE(spin_is_locked(&ctx->fault_wqh.lock)); +		VM_WARN_ON_ONCE(waitqueue_active(&ctx->fault_wqh)); +		VM_WARN_ON_ONCE(spin_is_locked(&ctx->event_wqh.lock)); +		VM_WARN_ON_ONCE(waitqueue_active(&ctx->event_wqh)); +		VM_WARN_ON_ONCE(spin_is_locked(&ctx->fd_wqh.lock)); +		VM_WARN_ON_ONCE(waitqueue_active(&ctx->fd_wqh));  		mmdrop(ctx->mm);  		kmem_cache_free(userfaultfd_ctx_cachep, ctx);  	} @@ -304,7 +304,7 @@ again:  		goto out;  	ret = false; -	if (!pmd_present(_pmd) || pmd_devmap(_pmd)) +	if (!pmd_present(_pmd))  		goto out;  	if (pmd_trans_huge(_pmd)) { @@ -383,12 +383,12 @@ vm_fault_t handle_userfault(struct vm_fault *vmf, unsigned long reason)  	if (!ctx)  		goto out; -	BUG_ON(ctx->mm != mm); +	VM_WARN_ON_ONCE(ctx->mm != mm);  	/* Any unrecognized flag is a bug. */ -	VM_BUG_ON(reason & ~__VM_UFFD_FLAGS); +	VM_WARN_ON_ONCE(reason & ~__VM_UFFD_FLAGS);  	/* 0 or > 1 flags set is a bug; we expect exactly 1. */ -	VM_BUG_ON(!reason || (reason & (reason - 1))); +	VM_WARN_ON_ONCE(!reason || (reason & (reason - 1)));  	if (ctx->features & UFFD_FEATURE_SIGBUS)  		goto out; @@ -411,12 +411,11 @@ vm_fault_t handle_userfault(struct vm_fault *vmf, unsigned long reason)  		 * to be sure not to return SIGBUS erroneously on  		 * nowait invocations.  		 */ -		BUG_ON(vmf->flags & FAULT_FLAG_RETRY_NOWAIT); +		VM_WARN_ON_ONCE(vmf->flags & FAULT_FLAG_RETRY_NOWAIT);  #ifdef CONFIG_DEBUG_VM  		if (printk_ratelimit()) { -			printk(KERN_WARNING -			       "FAULT_FLAG_ALLOW_RETRY missing %x\n", -			       vmf->flags); +			pr_warn("FAULT_FLAG_ALLOW_RETRY missing %x\n", +				vmf->flags);  			dump_stack();  		}  #endif @@ -602,7 +601,7 @@ static void userfaultfd_event_wait_completion(struct userfaultfd_ctx *ctx,  	 */  out:  	atomic_dec(&ctx->mmap_changing); -	VM_BUG_ON(atomic_read(&ctx->mmap_changing) < 0); +	VM_WARN_ON_ONCE(atomic_read(&ctx->mmap_changing) < 0);  	userfaultfd_ctx_put(ctx);  } @@ -710,7 +709,7 @@ void dup_userfaultfd_fail(struct list_head *fcs)  		struct userfaultfd_ctx *ctx = fctx->new;  		atomic_dec(&octx->mmap_changing); -		VM_BUG_ON(atomic_read(&octx->mmap_changing) < 0); +		VM_WARN_ON_ONCE(atomic_read(&octx->mmap_changing) < 0);  		userfaultfd_ctx_put(octx);  		userfaultfd_ctx_put(ctx); @@ -751,11 +750,6 @@ void mremap_userfaultfd_complete(struct vm_userfaultfd_ctx *vm_ctx,  	if (!ctx)  		return; -	if (to & ~PAGE_MASK) { -		userfaultfd_ctx_put(ctx); -		return; -	} -  	msg_init(&ewq.msg);  	ewq.msg.event = UFFD_EVENT_REMAP; @@ -766,6 +760,16 @@ void mremap_userfaultfd_complete(struct vm_userfaultfd_ctx *vm_ctx,  	userfaultfd_event_wait_completion(ctx, &ewq);  } +void mremap_userfaultfd_fail(struct vm_userfaultfd_ctx *vm_ctx) +{ +	struct userfaultfd_ctx *ctx = vm_ctx->ctx; + +	if (!ctx) +		return; + +	userfaultfd_ctx_put(ctx); +} +  bool userfaultfd_remove(struct vm_area_struct *vma,  			unsigned long start, unsigned long end)  { @@ -1243,7 +1247,7 @@ static int userfaultfd_register(struct userfaultfd_ctx *ctx,  	int ret;  	struct uffdio_register uffdio_register;  	struct uffdio_register __user *user_uffdio_register; -	unsigned long vm_flags; +	vm_flags_t vm_flags;  	bool found;  	bool basic_ioctls;  	unsigned long start, end; @@ -1317,8 +1321,8 @@ static int userfaultfd_register(struct userfaultfd_ctx *ctx,  	do {  		cond_resched(); -		BUG_ON(!!cur->vm_userfaultfd_ctx.ctx ^ -		       !!(cur->vm_flags & __VM_UFFD_FLAGS)); +		VM_WARN_ON_ONCE(!!cur->vm_userfaultfd_ctx.ctx ^ +				!!(cur->vm_flags & __VM_UFFD_FLAGS));  		/* check not compatible vmas */  		ret = -EINVAL; @@ -1372,7 +1376,7 @@ static int userfaultfd_register(struct userfaultfd_ctx *ctx,  		found = true;  	} for_each_vma_range(vmi, cur, end); -	BUG_ON(!found); +	VM_WARN_ON_ONCE(!found);  	ret = userfaultfd_register_range(ctx, vma, vm_flags, start, end,  					 wp_async); @@ -1464,8 +1468,16 @@ static int userfaultfd_unregister(struct userfaultfd_ctx *ctx,  	do {  		cond_resched(); -		BUG_ON(!!cur->vm_userfaultfd_ctx.ctx ^ -		       !!(cur->vm_flags & __VM_UFFD_FLAGS)); +		VM_WARN_ON_ONCE(!!cur->vm_userfaultfd_ctx.ctx ^ +				!!(cur->vm_flags & __VM_UFFD_FLAGS)); + +		/* +		 * Prevent unregistering through a different userfaultfd than +		 * the one used for registration. +		 */ +		if (cur->vm_userfaultfd_ctx.ctx && +		    cur->vm_userfaultfd_ctx.ctx != ctx) +			goto out_unlock;  		/*  		 * Check not compatible vmas, not strictly required @@ -1479,7 +1491,7 @@ static int userfaultfd_unregister(struct userfaultfd_ctx *ctx,  		found = true;  	} for_each_vma_range(vmi, cur, end); -	BUG_ON(!found); +	VM_WARN_ON_ONCE(!found);  	vma_iter_set(&vmi, start);  	prev = vma_prev(&vmi); @@ -1490,16 +1502,13 @@ static int userfaultfd_unregister(struct userfaultfd_ctx *ctx,  	for_each_vma_range(vmi, vma, end) {  		cond_resched(); -		BUG_ON(!vma_can_userfault(vma, vma->vm_flags, wp_async)); - -		/* -		 * Nothing to do: this vma is already registered into this -		 * userfaultfd and with the right tracking mode too. -		 */ +		/* VMA not registered with userfaultfd. */  		if (!vma->vm_userfaultfd_ctx.ctx)  			goto skip; -		WARN_ON(!(vma->vm_flags & VM_MAYWRITE)); +		VM_WARN_ON_ONCE(vma->vm_userfaultfd_ctx.ctx != ctx); +		VM_WARN_ON_ONCE(!vma_can_userfault(vma, vma->vm_flags, wp_async)); +		VM_WARN_ON_ONCE(!(vma->vm_flags & VM_MAYWRITE));  		if (vma->vm_start > start)  			start = vma->vm_start; @@ -1564,7 +1573,7 @@ static int userfaultfd_wake(struct userfaultfd_ctx *ctx,  	 * len == 0 means wake all and we don't want to wake all here,  	 * so check it again to be sure.  	 */ -	VM_BUG_ON(!range.len); +	VM_WARN_ON_ONCE(!range.len);  	wake_userfault(ctx, &range);  	ret = 0; @@ -1621,7 +1630,7 @@ static int userfaultfd_copy(struct userfaultfd_ctx *ctx,  		return -EFAULT;  	if (ret < 0)  		goto out; -	BUG_ON(!ret); +	VM_WARN_ON_ONCE(!ret);  	/* len == 0 would wake all */  	range.len = ret;  	if (!(uffdio_copy.mode & UFFDIO_COPY_MODE_DONTWAKE)) { @@ -1676,7 +1685,7 @@ static int userfaultfd_zeropage(struct userfaultfd_ctx *ctx,  	if (ret < 0)  		goto out;  	/* len == 0 would wake all */ -	BUG_ON(!ret); +	VM_WARN_ON_ONCE(!ret);  	range.len = ret;  	if (!(uffdio_zeropage.mode & UFFDIO_ZEROPAGE_MODE_DONTWAKE)) {  		range.start = uffdio_zeropage.range.start; @@ -1788,7 +1797,7 @@ static int userfaultfd_continue(struct userfaultfd_ctx *ctx, unsigned long arg)  		goto out;  	/* len == 0 would wake all */ -	BUG_ON(!ret); +	VM_WARN_ON_ONCE(!ret);  	range.len = ret;  	if (!(uffdio_continue.mode & UFFDIO_CONTINUE_MODE_DONTWAKE)) {  		range.start = uffdio_continue.range.start; @@ -1845,7 +1854,7 @@ static inline int userfaultfd_poison(struct userfaultfd_ctx *ctx, unsigned long  		goto out;  	/* len == 0 would wake all */ -	BUG_ON(!ret); +	VM_WARN_ON_ONCE(!ret);  	range.len = ret;  	if (!(uffdio_poison.mode & UFFDIO_POISON_MODE_DONTWAKE)) {  		range.start = uffdio_poison.range.start; @@ -2106,12 +2115,10 @@ static int new_userfaultfd(int flags)  	struct file *file;  	int fd; -	BUG_ON(!current->mm); +	VM_WARN_ON_ONCE(!current->mm);  	/* Check the UFFD_* constants for consistency.  */  	BUILD_BUG_ON(UFFD_USER_MODE_ONLY & UFFD_SHARED_FCNTL_FLAGS); -	BUILD_BUG_ON(UFFD_CLOEXEC != O_CLOEXEC); -	BUILD_BUG_ON(UFFD_NONBLOCK != O_NONBLOCK);  	if (flags & ~(UFFD_SHARED_FCNTL_FLAGS | UFFD_USER_MODE_ONLY))  		return -EINVAL; | 
