KVM: SVM: Remove VM from the GA Log notifier list before VM destruction

When a VM is being destroyed, delete it from the list used to process GA
Log interrupts 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.

Opportunistically use guard() to avoid a local "flags" variable.

Fixes: 5881f73757 ("svm: Introduce AMD IOMMU avic_ga_log_notifier")
Cc: Naveen N Rao (AMD) <naveen@kernel.org>
Cc: Xiao Wu <xiaowu.417@qq.com>
Reviewed-by: Naveen N Rao (AMD) <naveen@kernel.org>
Link: https://patch.msgid.link/20260630210156.457151-4-seanjc@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>
This commit is contained in:
Sean Christopherson 2026-06-30 14:01:56 -07:00
parent 4fa9a3767d
commit 78684b65fc
3 changed files with 15 additions and 7 deletions

View File

@ -374,9 +374,20 @@ int avic_vcpu_precreate(struct kvm *kvm)
return 0;
}
void avic_vm_pre_destroy(struct kvm *kvm)
{
struct kvm_svm *kvm_svm = to_kvm_svm(kvm);
if (WARN_ON_ONCE(!enable_apicv) || !kvm_svm->avic_vm_id)
return;
guard(spinlock_irqsave)(&svm_vm_data_hash_lock);
hash_del(&kvm_svm->hnode);
}
void avic_vm_destroy(struct kvm *kvm)
{
unsigned long flags;
struct kvm_svm *kvm_svm = to_kvm_svm(kvm);
if (!enable_apicv)
@ -385,12 +396,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));
if (kvm_svm->avic_vm_id) {
spin_lock_irqsave(&svm_vm_data_hash_lock, flags);
hash_del(&kvm_svm->hnode);
spin_unlock_irqrestore(&svm_vm_data_hash_lock, flags);
}
}
static phys_addr_t avic_get_backing_page_address(struct vcpu_svm *svm)

View File

@ -5329,6 +5329,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,
@ -5702,6 +5703,7 @@ static __init int svm_hardware_setup(void)
if (!enable_apicv) {
enable_ipiv = false;
svm_x86_ops.vcpu_precreate = NULL;
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;

View File

@ -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_vcpu_precreate(struct kvm *kvm);
void avic_vm_pre_destroy(struct kvm *kvm);
void avic_vm_destroy(struct kvm *kvm);
void avic_init_vmcb(struct vcpu_svm *svm, struct vmcb *vmcb);
int avic_incomplete_ipi_interception(struct kvm_vcpu *vcpu);