[PATCH 2/2] KVM: SVM: Re-check enc_context_owner under the owner's lock in sev_vm_destroy()

Shen Yongchao <[email protected]> Tue, 4 Aug 2026 23:00:30 +0800
Newsgroups org.kernel.vger.kvm
Message-ID <[email protected]>
sev_vm_destroy() reads the mirror KVM's enc_context_owner without any
synchronization and then locks the returned KVM.  A concurrent
KVM_SEV_MIGRATE_VM (sev_migrate_from()) transfers this mirror's
reference to a new owner under the current owner's lock, so the
previous owner can be freed between the unlocked read and
mutex_lock(): the mirror's final reference can be dropped while
sev_vm_destroy() is about to dereference the owner, dropping the
owner's reference count below its true value so that kvm_destroy_vm()
runs while the owner's fd is still open.

All kernels with SEV mirroring and intra-host migration support
(KVM_SEV_MIRROR_CREATE / KVM_SEV_MIGRATE_VM) are affected.  The mirror
list mechanism was introduced by commit b2125513dfc0 ("KVM: SEV: Allow
SEV intra-host migration of VM with mirrors", v5.18, 2022-02-18);
upstream mainline is unfixed as of 2026-08-04.  Triggering requires an
AMD SEV-capable host with CONFIG_KVM_AMD_SEV and a local user able to
create an SEV VM plus an SEV mirror VM, racing close() of the mirror
against KVM_SEV_MIGRATE_VM on the owner (function-level window,
deterministically reachable with a close(fd) primitive).

Take the owner's lock and re-check enc_context_owner: if it changed,
the migration already moved the mirror entry and the reference to the
new owner, so retry with the new owner.  The owner cannot be freed
while the mirror holds a reference to it, or while a migration that
takes the reference over holds the owner's file reference.

This patch was drafted with AI assistance.

Fixes: b2125513dfc0 ("KVM: SEV: Allow SEV intra-host migration of VM with mirrors")
Cc: [email protected]
Assisted-by: Hermes:deepseek-v4-flash
Signed-off-by: Shen Yongchao <[email protected]>
---
Reproducer: static analysis only (no SEV-capable hardware available);
no reproducer has been run.  The triggering sequence is described in
the commit message.

Mitigations: none; SEV is an optional platform feature and the attack
requires a local user with SEV VM creation privileges.  The list-side
window that remains when a mirror destroy races a migration of the
owner is serialized by patch 1/2 of this series.
 arch/x86/kvm/svm/sev.c | 26 ++++++++++++++++++++++++--
 1 file changed, 24 insertions(+), 2 deletions(-)

diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index 4fa4e9bf408..9cae4c1eeda 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -2969,9 +2969,31 @@ void sev_vm_destroy(struct kvm *kvm)
 	 * Note, mirror VMs don't support registering encrypted regions.
 	 */
 	if (is_mirroring_enc_context(kvm)) {
-		struct kvm *owner_kvm = sev->enc_context_owner;
+		struct kvm *owner_kvm;
+
+		/*
+		 * sev_migrate_from() can transfer this mirror's reference to a
+		 * new owner while holding the current owner's lock.  Take the
+		 * owner's lock and re-check enc_context_owner: if it changed,
+		 * the migration already moved the mirror entry and the reference
+		 * to the new owner, so retry with the new owner.  The owner
+		 * cannot be freed while this mirror holds a reference to it, or
+		 * while a migration that takes the reference over holds the
+		 * owner's file reference.
+		 */
+		for (;;) {
+			owner_kvm = READ_ONCE(sev->enc_context_owner);
+
+			/* Defensive; mirrors always hold an owner. */
+			if (!owner_kvm)
+				return;
+
+			mutex_lock(&owner_kvm->lock);
+			if (READ_ONCE(sev->enc_context_owner) == owner_kvm)
+				break;
+			mutex_unlock(&owner_kvm->lock);
+		}
 
-		mutex_lock(&owner_kvm->lock);
 		list_del(&sev->mirror_entry);
 		mutex_unlock(&owner_kvm->lock);
 		kvm_put_kvm(owner_kvm);
-- 
2.43.0