[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