From: Sean Christopherson <seanjc@google.com>
To: Sean Christopherson <seanjc@google.com>,
Paolo Bonzini <pbonzini@redhat.com>
Cc: kvm@vger.kernel.org, linux-kernel@vger.kernel.org,
Naveen N Rao <naveen@kernel.org>
Subject: [PATCH 1/2] KVM: SVM: Remove VM from the GA Log notifier list before VM destruction
Date: Thu, 25 Jun 2026 15:09:32 -0700 [thread overview]
Message-ID: <20260625220933.3357733-2-seanjc@google.com> (raw)
In-Reply-To: <20260625220933.3357733-1-seanjc@google.com>
When a VM is being destroyed, delete it from the list used to process GA
Log interrupt before vCPUs are freed, otherwise avic_ga_log_notifier()
could theoretically hit a use-after-free if a GA Log notification arrives
for a vCPU after the last reference to the VM has been put.
Note, in practice, it's likely all but impossible to trigger UAF, as all
all irqfds and thus all IRTEs are cleaned up by:
kvm_irqfd_release()
|
|-> irqfd_deactivate()
|
|-> irqfd_shutdown()
|
|-> irq_bypass_unregister_consumer()
And kvm_irqfd_release() is guaranteed to run before the last reference to
the VM is put. KVM also configures GA Log interrupts only when a vCPU is
blocking (older versions of KVM configre GA Log interrupts at all times,
but AVIC is off by default on those kernels). Hitting UAF would require
tearing down a VM shortly after a vCPU stopped blocking, and with a very,
very delayed IRQ from hardware.
Note, calling avic_vm_pre_destroy() if avic_vm_init() fails is unnecessary,
as the VM hasn't yet been added to the list (the VM structure is zeroed on
allocation, and so hash_del() is a nop). In fact, doing avic_vm_destroy()
at all on init failure is unnecessary now that the physical ID table is
allocated elsewhere; that will soon be remedied.
Opportunistically use guard() to avoid a local "flags" variable.
Fixes: 5881f73757cc ("svm: Introduce AMD IOMMU avic_ga_log_notifier")
Cc: stable@vger.kernel.org
Cc: Naveen N Rao (AMD) <naveen@kernel.org>
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
arch/x86/kvm/svm/avic.c | 15 ++++++++++-----
arch/x86/kvm/svm/svm.c | 2 ++
arch/x86/kvm/svm/svm.h | 1 +
3 files changed, 13 insertions(+), 5 deletions(-)
diff --git a/arch/x86/kvm/svm/avic.c b/arch/x86/kvm/svm/avic.c
index 58e493a80cb0..6b100fb014a6 100644
--- a/arch/x86/kvm/svm/avic.c
+++ b/arch/x86/kvm/svm/avic.c
@@ -311,9 +311,18 @@ int avic_alloc_physical_id_table(struct kvm *kvm)
return 0;
}
+void avic_vm_pre_destroy(struct kvm *kvm)
+{
+ if (WARN_ON_ONCE(!enable_apicv))
+ return;
+
+ guard(spinlock_irqsave)(&svm_vm_data_hash_lock);
+
+ hash_del(&to_kvm_svm(kvm)->hnode);
+}
+
void avic_vm_destroy(struct kvm *kvm)
{
- unsigned long flags;
struct kvm_svm *kvm_svm = to_kvm_svm(kvm);
if (!enable_apicv)
@@ -322,10 +331,6 @@ void avic_vm_destroy(struct kvm *kvm)
free_page((unsigned long)kvm_svm->avic_logical_id_table);
free_pages((unsigned long)kvm_svm->avic_physical_id_table,
avic_get_physical_id_table_order(kvm));
-
- spin_lock_irqsave(&svm_vm_data_hash_lock, flags);
- hash_del(&kvm_svm->hnode);
- spin_unlock_irqrestore(&svm_vm_data_hash_lock, flags);
}
int avic_vm_init(struct kvm *kvm)
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index ef69a51ab27f..91b1e582d16f 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -5340,6 +5340,7 @@ struct kvm_x86_ops svm_x86_ops __initdata = {
.vm_size = sizeof(struct kvm_svm),
.vm_init = svm_vm_init,
+ .vm_pre_destroy = avic_vm_pre_destroy,
.vm_destroy = svm_vm_destroy,
.prepare_switch_to_guest = svm_prepare_switch_to_guest,
@@ -5712,6 +5713,7 @@ static __init int svm_hardware_setup(void)
enable_apicv = avic_hardware_setup();
if (!enable_apicv) {
enable_ipiv = false;
+ svm_x86_ops.vm_pre_destroy = NULL;
svm_x86_ops.vcpu_blocking = NULL;
svm_x86_ops.vcpu_unblocking = NULL;
svm_x86_ops.vcpu_get_apicv_inhibit_reasons = NULL;
diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
index 716be21fba33..a25f8994b877 100644
--- a/arch/x86/kvm/svm/svm.h
+++ b/arch/x86/kvm/svm/svm.h
@@ -946,6 +946,7 @@ extern struct kvm_x86_nested_ops svm_nested_ops;
bool __init avic_hardware_setup(void);
void avic_hardware_unsetup(void);
int avic_alloc_physical_id_table(struct kvm *kvm);
+void avic_vm_pre_destroy(struct kvm *kvm);
void avic_vm_destroy(struct kvm *kvm);
int avic_vm_init(struct kvm *kvm);
void avic_init_vmcb(struct vcpu_svm *svm, struct vmcb *vmcb);
--
2.55.0.rc0.799.gd6f94ed593-goog
next prev parent reply other threads:[~2026-06-25 22:09 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-25 22:09 [PATCH 0/2] KVM: SVM: Fix a (very) unlikely UAF for GA Log IRQs Sean Christopherson
2026-06-25 22:09 ` Sean Christopherson [this message]
2026-06-25 22:26 ` [PATCH 1/2] KVM: SVM: Remove VM from the GA Log notifier list before VM destruction sashiko-bot
2026-06-28 18:30 ` XIAO WU
2026-06-25 22:09 ` [PATCH 2/2] KVM: SVM: Drop unnecessary avic_vm_destroy() call on init failure Sean Christopherson
2026-06-25 22:25 ` sashiko-bot
2026-06-29 13:27 ` Naveen N Rao
2026-06-30 0:17 ` Sean Christopherson
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260625220933.3357733-2-seanjc@google.com \
--to=seanjc@google.com \
--cc=kvm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=naveen@kernel.org \
--cc=pbonzini@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox