kvm.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/4] KVM: guest_memfd: Fix binding bugs
@ 2026-09-02 18:20 Sean Christopherson
  2026-09-02 18:20 ` [PATCH v2 1/4] KVM: guest_memfd: Gracefully handle xarray errors when binding a memslot Sean Christopherson
                   ` (3 more replies)
  0 siblings, 4 replies; 9+ messages in thread
From: Sean Christopherson @ 2026-09-02 18:20 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini
  Cc: David Hildenbrand, kvm, linux-kernel, Stefan Teodorescu,
	Dennis Tighe, Sashiko Bot, Yan Zhao

Fix guest_memfd bugs related to binding to a memslot:

 - Handle errors when inserting into guest_memfd's binding xarray, e.g. to
   do the right thing on ENOMEM.

 - Bind a memslot only once the memslot is fully prepared (because it becomes
   reachable/visible once its inserted into gmem's bindings arraxy).

Patch 4 is a related cleanup to remove a superflous WRITE_ONCE() (unwinding
the slot update on insertion failure isn't an option if the slot is observable,
i.e. if the WRITE_ONCE() is actually necessary).

v2:
 - Fix the binding-too-early bug. [Sashiko]
 - Explicitly zero the bindings entry on failure to ensure there are no
   partial entries. [Sashiko]

v1: https://lore.kernel.org/all/20260826165154.766699-2-seanjc@google.com

Sean Christopherson (4):
  KVM: guest_memfd: Gracefully handle xarray errors when binding a
    memslot
  KVM: Use goto to handle errors during memslot preparation
  KVM: guest_memfd: Establish memslot<=>guest_memfd bindings *after*
    memslot is ready
  KVM: guest_memfd: Drop superfluous WRITE_ONCE() when binding a memslot

 virt/kvm/guest_memfd.c | 16 +++++++++--
 virt/kvm/kvm_main.c    | 63 +++++++++++++++++++++++++-----------------
 2 files changed, 50 insertions(+), 29 deletions(-)


base-commit: 76671054f9a1ff6abb976583cd8da37650acdc97
-- 
2.55.0.970.g62bdec98f9-goog


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v2 1/4] KVM: guest_memfd: Gracefully handle xarray errors when binding a memslot
  2026-09-02 18:20 [PATCH v2 0/4] KVM: guest_memfd: Fix binding bugs Sean Christopherson
@ 2026-09-02 18:20 ` Sean Christopherson
  2026-09-02 18:32   ` sashiko-bot
  2026-09-02 18:20 ` [PATCH v2 2/4] KVM: Use goto to handle errors during memslot preparation Sean Christopherson
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 9+ messages in thread
From: Sean Christopherson @ 2026-09-02 18:20 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini
  Cc: David Hildenbrand, kvm, linux-kernel, Stefan Teodorescu,
	Dennis Tighe, Sashiko Bot, Yan Zhao

If inserting a memslot into a guest_memfd's bindings xarray fails,
propagate the error back to the caller, i.e. fail memslot creation as well.
Signalling success and continuing on with memslot creation results in
use-after-free, as the guest_memfd instance will remain reachable via the
memslot after the file is freed (kvm_gmem_release() won't nullify the file
pointer due to lack of a valid binding).

Opportunistically WARN and reject binding if KVM_MEMSLOT_GMEM_ONLY is
already set, partly to guard against goofs elsewhere, but mostly so that
KVM doesn't need to worry about clobbering flags when unwinding on failure.

Fixes: a7800aa80ea4 ("KVM: Add KVM_CREATE_GUEST_MEMFD ioctl() for guest-specific backing memory")
Cc: stable@vger.kernel.org
Reported-by: Stefan Teodorescu <fane@google.com>
Reported-by: Dennis Tighe <dtighe@google.com>
Reported-by: Sashiko Bot <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/all/20260823135031.4F6DC1F000E9%40smtp.kernel.org
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
 virt/kvm/guest_memfd.c | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
index b596486d184c..2c8d8735de5f 100644
--- a/virt/kvm/guest_memfd.c
+++ b/virt/kvm/guest_memfd.c
@@ -612,10 +612,14 @@ int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot,
 	struct inode *inode;
 	struct file *file;
 	int r = -EINVAL;
+	void *xar;
 
 	BUILD_BUG_ON(sizeof(gpa_t) != sizeof(offset));
 	BUILD_BUG_ON(sizeof(gfn_t) != sizeof(slot->gmem.pgoff));
 
+	if (WARN_ON_ONCE(slot->flags & KVM_MEMSLOT_GMEM_ONLY))
+		return -EINVAL;
+
 	file = fget(fd);
 	if (!file)
 		return -EBADF;
@@ -654,7 +658,7 @@ int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot,
 	if (kvm_gmem_supports_mmap(inode))
 		slot->flags |= KVM_MEMSLOT_GMEM_ONLY;
 
-	xa_store_range(&f->bindings, start, end - 1, slot, GFP_KERNEL);
+	xar = xa_store_range(&f->bindings, start, end - 1, slot, GFP_KERNEL);
 	filemap_invalidate_unlock(inode->i_mapping);
 
 	/*
@@ -662,7 +666,13 @@ int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot,
 	 * not the other way 'round.  Active bindings are invalidated if the
 	 * file is closed before memslots are destroyed.
 	 */
-	r = 0;
+	r = xa_is_err(xar) ? xa_err(xar) : 0;
+	if (r) {
+		xa_store_range(&f->bindings, start, end - 1, NULL, GFP_KERNEL);
+		slot->gmem.file = NULL;
+		slot->gmem.pgoff = 0;
+		slot->flags &= ~KVM_MEMSLOT_GMEM_ONLY;
+	}
 err:
 	fput(file);
 	return r;
-- 
2.55.0.970.g62bdec98f9-goog


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v2 2/4] KVM: Use goto to handle errors during memslot preparation
  2026-09-02 18:20 [PATCH v2 0/4] KVM: guest_memfd: Fix binding bugs Sean Christopherson
  2026-09-02 18:20 ` [PATCH v2 1/4] KVM: guest_memfd: Gracefully handle xarray errors when binding a memslot Sean Christopherson
@ 2026-09-02 18:20 ` Sean Christopherson
  2026-09-02 18:20 ` [PATCH v2 3/4] KVM: guest_memfd: Establish memslot<=>guest_memfd bindings *after* memslot is ready Sean Christopherson
  2026-09-02 18:20 ` [PATCH v2 4/4] KVM: guest_memfd: Drop superfluous WRITE_ONCE() when binding a memslot Sean Christopherson
  3 siblings, 0 replies; 9+ messages in thread
From: Sean Christopherson @ 2026-09-02 18:20 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini
  Cc: David Hildenbrand, kvm, linux-kernel, Stefan Teodorescu,
	Dennis Tighe, Sashiko Bot, Yan Zhao

Use a goto to unwind early memslot changes if preparing for a memslot
operation fails.  This will allow moving the creation of guest_memfd
bindings into kvm_set_memslot() without needing to copy+paste the unwind
logic.

No functional change intended.

Signed-off-by: Sean Christopherson <seanjc@google.com>
---
 virt/kvm/kvm_main.c | 31 ++++++++++++++++---------------
 1 file changed, 16 insertions(+), 15 deletions(-)

diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 65eb26a0520d..3c0dbe60a5b4 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -1931,21 +1931,8 @@ static int kvm_set_memslot(struct kvm *kvm,
 	}
 
 	r = kvm_prepare_memory_region(kvm, old, new, change);
-	if (r) {
-		/*
-		 * For DELETE/MOVE, revert the above INVALID change.  No
-		 * modifications required since the original slot was preserved
-		 * in the inactive slots.  Changing the active memslots also
-		 * release slots_arch_lock.
-		 */
-		if (change == KVM_MR_DELETE || change == KVM_MR_MOVE) {
-			kvm_activate_memslot(kvm, invalid_slot, old);
-			kfree(invalid_slot);
-		} else {
-			mutex_unlock(&kvm->slots_arch_lock);
-		}
-		return r;
-	}
+	if (r)
+		goto err;
 
 	/*
 	 * For DELETE and MOVE, the working slot is now active as the INVALID
@@ -1977,6 +1964,20 @@ static int kvm_set_memslot(struct kvm *kvm,
 	kvm_commit_memory_region(kvm, old, new, change);
 
 	return 0;
+
+err:
+	/*
+	 * For DELETE/MOVE, revert the above INVALID change.  No modifications
+	 * required since the original slot was preserved in the inactive slots.
+	 * Changing the active memslots also release slots_arch_lock.
+	 */
+	if (change == KVM_MR_DELETE || change == KVM_MR_MOVE) {
+		kvm_activate_memslot(kvm, invalid_slot, old);
+		kfree(invalid_slot);
+	} else {
+		mutex_unlock(&kvm->slots_arch_lock);
+	}
+	return r;
 }
 
 static bool kvm_check_memslot_overlap(struct kvm_memslots *slots, int id,
-- 
2.55.0.970.g62bdec98f9-goog


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v2 3/4] KVM: guest_memfd: Establish memslot<=>guest_memfd bindings *after* memslot is ready
  2026-09-02 18:20 [PATCH v2 0/4] KVM: guest_memfd: Fix binding bugs Sean Christopherson
  2026-09-02 18:20 ` [PATCH v2 1/4] KVM: guest_memfd: Gracefully handle xarray errors when binding a memslot Sean Christopherson
  2026-09-02 18:20 ` [PATCH v2 2/4] KVM: Use goto to handle errors during memslot preparation Sean Christopherson
@ 2026-09-02 18:20 ` Sean Christopherson
  2026-09-02 18:35   ` sashiko-bot
  2026-09-02 18:20 ` [PATCH v2 4/4] KVM: guest_memfd: Drop superfluous WRITE_ONCE() when binding a memslot Sean Christopherson
  3 siblings, 1 reply; 9+ messages in thread
From: Sean Christopherson @ 2026-09-02 18:20 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini
  Cc: David Hildenbrand, kvm, linux-kernel, Stefan Teodorescu,
	Dennis Tighe, Sashiko Bot, Yan Zhao

Wait to bind a memslot to a guest_memfd instance until *after* the memslot
is fully prepared, as creating the binding in guest_memfd will effectively
expose the memslot to readers.  As pointed out by Sashiko, binding the
memslot before it's ready to be exposed to the rest of the world can break
various memslot assumption and rules.  E.g. x86 could observe a NULL rmap
pointer if a PUNCH_HOLE hit the guest_memfd after the binding was created,
but before KVM made it through kvm_prepare_memory_region().

Begrudgingly resort to passing in the guest_memfd fd+offset pair to
kvm_set_memslot(), as creating the binding really does need to happen in
the middle of setting the new memslot.  Alternatively, to preserve the
aesthetically pleasing function prototype, "struct kvm_memory_slot" could
be expanded to track the fd and the file, but that would create the
possibility for TOCTOU bugs on the fd vs. file, and would add zero value
beyond making kvm_set_memslot() look pretty.

Fixes:a7800aa80ea4 ("KVM: Add KVM_CREATE_GUEST_MEMFD ioctl() for guest-specific backing memory")
Cc: stable@vger.kernel.org
Reported-by: Sashiko Bot <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/all/20260826170551.BEF801F000E9@smtp.kernel.org
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
 virt/kvm/kvm_main.c | 34 ++++++++++++++++++++++------------
 1 file changed, 22 insertions(+), 12 deletions(-)

diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 3c0dbe60a5b4..21c10cbbac66 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -1887,7 +1887,8 @@ static void kvm_update_flags_memslot(struct kvm *kvm,
 static int kvm_set_memslot(struct kvm *kvm,
 			   struct kvm_memory_slot *old,
 			   struct kvm_memory_slot *new,
-			   enum kvm_mr_change change)
+			   enum kvm_mr_change change,
+			   unsigned int gmem_fd, uoff_t gmem_offset)
 {
 	struct kvm_memory_slot *invalid_slot;
 	int r;
@@ -1934,6 +1935,15 @@ static int kvm_set_memslot(struct kvm *kvm,
 	if (r)
 		goto err;
 
+	if (new && new->flags & KVM_MEM_GUEST_MEMFD) {
+		if (WARN_ON_ONCE(change != KVM_MR_CREATE))
+			goto err_bind;
+
+		r = kvm_gmem_bind(kvm, new, gmem_fd, gmem_offset);
+		if (r)
+			goto err_bind;
+	}
+
 	/*
 	 * For DELETE and MOVE, the working slot is now active as the INVALID
 	 * version of the old slot.  MOVE is particularly special as it reuses
@@ -1965,6 +1975,13 @@ static int kvm_set_memslot(struct kvm *kvm,
 
 	return 0;
 
+err_bind:
+	if (new) {
+		kvm_arch_free_memslot(kvm, new);
+
+		if (new->dirty_bitmap && (!old || !old->dirty_bitmap))
+			kvm_destroy_dirty_bitmap(new);
+	}
 err:
 	/*
 	 * For DELETE/MOVE, revert the above INVALID change.  No modifications
@@ -2059,7 +2076,7 @@ static int kvm_set_memory_region(struct kvm *kvm,
 		if (WARN_ON_ONCE(kvm->nr_memslot_pages < old->npages))
 			return -EIO;
 
-		return kvm_set_memslot(kvm, old, NULL, KVM_MR_DELETE);
+		return kvm_set_memslot(kvm, old, NULL, KVM_MR_DELETE, -1, 0);
 	}
 
 	base_gfn = (mem->guest_phys_addr >> PAGE_SHIFT);
@@ -2106,21 +2123,14 @@ static int kvm_set_memory_region(struct kvm *kvm,
 	new->npages = npages;
 	new->flags = mem->flags;
 	new->userspace_addr = mem->userspace_addr;
-	if (mem->flags & KVM_MEM_GUEST_MEMFD) {
-		r = kvm_gmem_bind(kvm, new, mem->guest_memfd, mem->guest_memfd_offset);
-		if (r)
-			goto out;
-	}
 
-	r = kvm_set_memslot(kvm, old, new, change);
+	r = kvm_set_memslot(kvm, old, new, change,
+			    mem->guest_memfd, mem->guest_memfd_offset);
 	if (r)
-		goto out_unbind;
+		goto out;
 
 	return 0;
 
-out_unbind:
-	if (mem->flags & KVM_MEM_GUEST_MEMFD)
-		kvm_gmem_unbind(new);
 out:
 	kfree(new);
 	return r;
-- 
2.55.0.970.g62bdec98f9-goog


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v2 4/4] KVM: guest_memfd: Drop superfluous WRITE_ONCE() when binding a memslot
  2026-09-02 18:20 [PATCH v2 0/4] KVM: guest_memfd: Fix binding bugs Sean Christopherson
                   ` (2 preceding siblings ...)
  2026-09-02 18:20 ` [PATCH v2 3/4] KVM: guest_memfd: Establish memslot<=>guest_memfd bindings *after* memslot is ready Sean Christopherson
@ 2026-09-02 18:20 ` Sean Christopherson
  2026-09-02 18:36   ` sashiko-bot
  3 siblings, 1 reply; 9+ messages in thread
From: Sean Christopherson @ 2026-09-02 18:20 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini
  Cc: David Hildenbrand, kvm, linux-kernel, Stefan Teodorescu,
	Dennis Tighe, Sashiko Bot, Yan Zhao

Drop the superfluous WRITE_ONCE() when setting a memslot's guest_memfd file
during initial binding, as the memslot *must* be inactive and unreachable.
The superfluous WRITE_ONCE() was added by commit 67b43038ce14 ("KVM:
guest_memfd: Remove RCU-protected attribute from slot->gmem.file") to
maintain rough "parity" with the existing rcu_assign_pointer(), not
realizing that the only reason rcu_assign_pointer() was used was to make
sparse and other checkers happy.

Cc: Yan Zhao <yan.y.zhao@intel.com>
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
 virt/kvm/guest_memfd.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
index 2c8d8735de5f..323159c9b5f3 100644
--- a/virt/kvm/guest_memfd.c
+++ b/virt/kvm/guest_memfd.c
@@ -653,7 +653,7 @@ int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot,
 	 * kvm_gmem_bind() must occur on a new memslot.  Because the memslot
 	 * is not visible yet, kvm_gmem_get_pfn() is guaranteed to see the file.
 	 */
-	WRITE_ONCE(slot->gmem.file, file);
+	slot->gmem.file = file;
 	slot->gmem.pgoff = start;
 	if (kvm_gmem_supports_mmap(inode))
 		slot->flags |= KVM_MEMSLOT_GMEM_ONLY;
-- 
2.55.0.970.g62bdec98f9-goog


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH v2 1/4] KVM: guest_memfd: Gracefully handle xarray errors when binding a memslot
  2026-09-02 18:20 ` [PATCH v2 1/4] KVM: guest_memfd: Gracefully handle xarray errors when binding a memslot Sean Christopherson
@ 2026-09-02 18:32   ` sashiko-bot
  0 siblings, 0 replies; 9+ messages in thread
From: sashiko-bot @ 2026-09-02 18:32 UTC (permalink / raw)
  To: Sean Christopherson; +Cc: kvm

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Use-after-free and race condition in error path of kvm_gmem_bind()
--

commit 750801b0d7bf80e6bf0cad3be58657ec4af30b90
Author: Sean Christopherson <seanjc@google.com>

KVM: guest_memfd: Gracefully handle xarray errors when binding a memslot

This commit propagates errors from inserting a memslot into a guest_memfd's
bindings xarray back to the caller to prevent a use-after-free when the file
is released. It also opportunistically rejects bindings if
KVM_MEMSLOT_GMEM_ONLY is already set.

Link: https://lore.kernel.org/all/20260823135031.4F6DC1F000E9%40smtp.kernel.org

> diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
> index b596486d184ca..2c8d8735de5fe 100644
> --- a/virt/kvm/guest_memfd.c
> +++ b/virt/kvm/guest_memfd.c
[ ... ]
> @@ -654,7 +658,7 @@ int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot,
>  	if (kvm_gmem_supports_mmap(inode))
>  		slot->flags |= KVM_MEMSLOT_GMEM_ONLY;
>  
> -	xa_store_range(&f->bindings, start, end - 1, slot, GFP_KERNEL);
> +	xar = xa_store_range(&f->bindings, start, end - 1, slot, GFP_KERNEL);
>  	filemap_invalidate_unlock(inode->i_mapping);
>  
>  	/*
> @@ -662,7 +666,13 @@ int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot,
>  	 * not the other way 'round.  Active bindings are invalidated if the
>  	 * file is closed before memslots are destroyed.
>  	 */
> -	r = 0;
> +	r = xa_is_err(xar) ? xa_err(xar) : 0;
> +	if (r) {
> +		xa_store_range(&f->bindings, start, end - 1, NULL, GFP_KERNEL);

[Severity: High]
Does this error path modify the bindings XArray without holding the
filemap_invalidate_lock?

If the initial xa_store_range() fails (for example, returning -ENOMEM),
filemap_invalidate_unlock() has already been called just above.

Concurrently, if a thread calls fallocate(PUNCH_HOLE) on the guest_memfd,
it could acquire filemap_invalidate_lock, iterate the XArray, and observe
the partially inserted slot.

While fallocate() is passing the slot to kvm_mmu_unmap_gfn_range(), this
error path clears the binding locklessly and returns the error to
userspace (e.g. from KVM_SET_USER_MEMORY_REGION). This causes the slot to
be freed immediately by kvm_set_memory_region(). Can this result in a
use-after-free since fallocate() is concurrently dereferencing it?

> +		slot->gmem.file = NULL;
> +		slot->gmem.pgoff = 0;
> +		slot->flags &= ~KVM_MEMSLOT_GMEM_ONLY;
> +	}
>  err:
>  	fput(file);
>  	return r;

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260902182020.2615443-1-seanjc@google.com?part=1

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v2 3/4] KVM: guest_memfd: Establish memslot<=>guest_memfd bindings *after* memslot is ready
  2026-09-02 18:20 ` [PATCH v2 3/4] KVM: guest_memfd: Establish memslot<=>guest_memfd bindings *after* memslot is ready Sean Christopherson
@ 2026-09-02 18:35   ` sashiko-bot
  0 siblings, 0 replies; 9+ messages in thread
From: sashiko-bot @ 2026-09-02 18:35 UTC (permalink / raw)
  To: Sean Christopherson; +Cc: kvm

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [Critical] kvm_gmem_bind drops filemap_invalidate_lock before rolling back a failed xa_store_range, exposing a partially bound, not-yet-active memslot to concurrent PUNCH_HOLE operations and leading to a severe Use-After-Free on the error path.
--

commit cf9b1c6186b61c3381316f5121f13c09601e0b01
Author: Sean Christopherson <seanjc@google.com>
KVM: guest_memfd: Establish memslot<=>guest_memfd bindings *after* memslot is ready

This commit defers binding a memslot to a guest_memfd instance until the memslot
is fully prepared. It prevents exposing the memslot to readers prematurely,
which could break various memslot assumptions and rules.

Closes: https://lore.kernel.org/all/20260826170551.BEF801F000E9@smtp.kernel.org

> diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
> index 3c0dbe60a5b4f..21c10cbbac661 100644
> --- a/virt/kvm/kvm_main.c
> +++ b/virt/kvm/kvm_main.c
[ ... ]
> @@ -1934,6 +1935,15 @@ static int kvm_set_memslot(struct kvm *kvm,
>  	if (r)
>  		goto err;
>  
> +	if (new && new->flags & KVM_MEM_GUEST_MEMFD) {
> +		if (WARN_ON_ONCE(change != KVM_MR_CREATE))
> +			goto err_bind;
> +
> +		r = kvm_gmem_bind(kvm, new, gmem_fd, gmem_offset);
> +		if (r)
> +			goto err_bind;
> +	}
> +
>  	/*
>  	 * For DELETE and MOVE, the working slot is now active as the INVALID
[ ... ]
> @@ -1965,6 +1975,13 @@ static int kvm_set_memslot(struct kvm *kvm,
>  
>  	return 0;
>  
> +err_bind:
> +	if (new) {
> +		kvm_arch_free_memslot(kvm, new);
> +
> +		if (new->dirty_bitmap && (!old || !old->dirty_bitmap))
> +			kvm_destroy_dirty_bitmap(new);
> +	}

[Severity: Critical]
This is a pre-existing issue, but does moving kvm_gmem_bind() into this
sequence create an exploitable use-after-free on the error path?

Looking at kvm_gmem_bind(), if xa_store_range() fails, the lock is released
before rolling back the partial entry:

virt/kvm/guest_memfd.c:kvm_gmem_bind() {
    ...
	xar = xa_store_range(&f->bindings, start, end - 1, slot, GFP_KERNEL);
	filemap_invalidate_unlock(inode->i_mapping);

	r = xa_is_err(xar) ? xa_err(xar) : 0;
	if (r) {
		xa_store_range(&f->bindings, start, end - 1, NULL, GFP_KERNEL);
    ...
}

If a concurrent PUNCH_HOLE operation acquires the lock right after
filemap_invalidate_unlock(), could it extract the partially bound slot and
proceed to kvm_mmu_unmap_gfn_range()?

If so, while the concurrent thread is accessing the slot, this error path
executes kvm_arch_free_memslot(), and then the caller, kvm_set_memory_region(),
executes kfree(). Could this result in the PUNCH_HOLE thread operating on
completely freed memory?

>  err:
>  	/*
>  	 * For DELETE/MOVE, revert the above INVALID change.  No modifications

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260902182020.2615443-1-seanjc@google.com?part=3

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v2 4/4] KVM: guest_memfd: Drop superfluous WRITE_ONCE() when binding a memslot
  2026-09-02 18:20 ` [PATCH v2 4/4] KVM: guest_memfd: Drop superfluous WRITE_ONCE() when binding a memslot Sean Christopherson
@ 2026-09-02 18:36   ` sashiko-bot
  2026-09-02 19:00     ` Sean Christopherson
  0 siblings, 1 reply; 9+ messages in thread
From: sashiko-bot @ 2026-09-02 18:36 UTC (permalink / raw)
  To: Sean Christopherson; +Cc: kvm

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [Critical] Modifying the guest_memfd xarray outside of filemap_invalidate_lock on the error path causes a Use-After-Free and permanent MMU invalidation lockup.
--

commit c377e9963148427885842d70319259945c649a8a
Author: Sean Christopherson <seanjc@google.com>

KVM: guest_memfd: Drop superfluous WRITE_ONCE() when binding a memslot

Drop the superfluous WRITE_ONCE() when setting a memslot's guest_memfd file
during initial binding, as the memslot *must* be inactive and unreachable.
The superfluous WRITE_ONCE() was added by commit 67b43038ce14 to
maintain rough "parity" with the existing rcu_assign_pointer().

> diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
> index 2c8d8735de5fe..323159c9b5f34 100644
> --- a/virt/kvm/guest_memfd.c
> +++ b/virt/kvm/guest_memfd.c
> @@ -653,7 +653,7 @@ int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot,
>  	 * kvm_gmem_bind() must occur on a new memslot.  Because the memslot
>  	 * is not visible yet, kvm_gmem_get_pfn() is guaranteed to see the file.
>  	 */
> -	WRITE_ONCE(slot->gmem.file, file);
> +	slot->gmem.file = file;
>  	slot->gmem.pgoff = start;
>  	if (kvm_gmem_supports_mmap(inode))
>  		slot->flags |= KVM_MEMSLOT_GMEM_ONLY;

[Severity: High]
This isn't a bug introduced by this patch, but modifying the xarray outside
the filemap_invalidate_lock on the error path further down in this function
appears to cause a use-after-free and a permanent MMU invalidation lockup.

If xa_store_range() encounters -ENOMEM during the store, it can abort and
leave the slot partially inserted in the tree. kvm_gmem_bind() then explicitly
drops filemap_invalidate_lock before executing its error path. 

On the error path, it attempts to clean up by calling xa_store_range() with
NULL outside the lock:

    xar = xa_store_range(&f->bindings, start, end - 1, slot, GFP_KERNEL);
    filemap_invalidate_unlock(inode->i_mapping);

    r = xa_is_err(xar) ? xa_err(xar) : 0;
    if (r) {
        xa_store_range(&f->bindings, start, end - 1, NULL, GFP_KERNEL);

Concurrently, a PUNCH_HOLE operation runs __kvm_gmem_invalidate_start()
which takes filemap_invalidate_lock and iterates over the XArray.

Because the error path doesn't hold the lock, it can race with the iteration:

CPU1 (kvm_gmem_bind error path)
    if (r) {
        xa_store_range(&f->bindings, start, end - 1, NULL, GFP_KERNEL);
        ...

CPU2 (__kvm_gmem_invalidate_start)
    xa_for_each_range(&f->bindings, index, slot, start, end - 1) {
        ...

xa_for_each_range() does not establish an RCU read-side critical section for
the loop body. The error path removes the partially inserted slot and returns,
causing kvm_set_memory_region() to immediately kfree() the slot. The
PUNCH_HOLE iteration then dereferences the freed slot, causing a
use-after-free.

Additionally, the PUNCH_HOLE operation successfully called
kvm_mmu_invalidate_start(), but because the slot is removed by the racing
error path before __kvm_gmem_invalidate_end() executes, the closing
xa_find() check fails and kvm_mmu_invalidate_end() is permanently skipped.

Does the error path need to execute with filemap_invalidate_lock() held?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260902182020.2615443-1-seanjc@google.com?part=4

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v2 4/4] KVM: guest_memfd: Drop superfluous WRITE_ONCE() when binding a memslot
  2026-09-02 18:36   ` sashiko-bot
@ 2026-09-02 19:00     ` Sean Christopherson
  0 siblings, 0 replies; 9+ messages in thread
From: Sean Christopherson @ 2026-09-02 19:00 UTC (permalink / raw)
  To: sashiko-reviews; +Cc: kvm

On Wed, Sep 02, 2026, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> 
> Pre-existing issues:
> - [Critical] Modifying the guest_memfd xarray outside of filemap_invalidate_lock on the error path causes a Use-After-Free and permanent MMU invalidation lockup.
> --
> 
> commit c377e9963148427885842d70319259945c649a8a
> Author: Sean Christopherson <seanjc@google.com>
> 
> KVM: guest_memfd: Drop superfluous WRITE_ONCE() when binding a memslot
> 
> Drop the superfluous WRITE_ONCE() when setting a memslot's guest_memfd file
> during initial binding, as the memslot *must* be inactive and unreachable.
> The superfluous WRITE_ONCE() was added by commit 67b43038ce14 to
> maintain rough "parity" with the existing rcu_assign_pointer().
> 
> > diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
> > index 2c8d8735de5fe..323159c9b5f34 100644
> > --- a/virt/kvm/guest_memfd.c
> > +++ b/virt/kvm/guest_memfd.c
> > @@ -653,7 +653,7 @@ int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot,
> >  	 * kvm_gmem_bind() must occur on a new memslot.  Because the memslot
> >  	 * is not visible yet, kvm_gmem_get_pfn() is guaranteed to see the file.
> >  	 */
> > -	WRITE_ONCE(slot->gmem.file, file);
> > +	slot->gmem.file = file;
> >  	slot->gmem.pgoff = start;
> >  	if (kvm_gmem_supports_mmap(inode))
> >  		slot->flags |= KVM_MEMSLOT_GMEM_ONLY;
> 
> [Severity: High]
> This isn't a bug introduced by this patch, but modifying the xarray outside
> the filemap_invalidate_lock on the error path further down in this function
> appears to cause a use-after-free and a permanent MMU invalidation lockup.
> 
> If xa_store_range() encounters -ENOMEM during the store, it can abort and
> leave the slot partially inserted in the tree. kvm_gmem_bind() then explicitly
> drops filemap_invalidate_lock before executing its error path. 

LOL, I think I get the point...

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2026-09-02 19:00 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-02 18:20 [PATCH v2 0/4] KVM: guest_memfd: Fix binding bugs Sean Christopherson
2026-09-02 18:20 ` [PATCH v2 1/4] KVM: guest_memfd: Gracefully handle xarray errors when binding a memslot Sean Christopherson
2026-09-02 18:32   ` sashiko-bot
2026-09-02 18:20 ` [PATCH v2 2/4] KVM: Use goto to handle errors during memslot preparation Sean Christopherson
2026-09-02 18:20 ` [PATCH v2 3/4] KVM: guest_memfd: Establish memslot<=>guest_memfd bindings *after* memslot is ready Sean Christopherson
2026-09-02 18:35   ` sashiko-bot
2026-09-02 18:20 ` [PATCH v2 4/4] KVM: guest_memfd: Drop superfluous WRITE_ONCE() when binding a memslot Sean Christopherson
2026-09-02 18:36   ` sashiko-bot
2026-09-02 19:00     ` Sean Christopherson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).