KVM: x86: Reset IRTE to host control if *new* route isn't postable
commit 9bcac97dc42d2f4da8229d18feb0fe2b1ce523a2 upstream. Restore an IRTE back to host control (remapped or posted MSI mode) if the *new* GSI route prevents posting the IRQ directly to a vCPU, regardless of the GSI routing type. Updating the IRTE if and only if the new GSI is an MSI results in KVM leaving an IRTE posting to a vCPU. The dangling IRTE can result in interrupts being incorrectly delivered to the guest, and in the worst case scenario can result in use-after-free, e.g. if the VM is torn down, but the underlying host IRQ isn't freed. Fixes:efc644048e("KVM: x86: Update IRTE for posted-interrupts") Fixes:411b44ba80("svm: Implements update_pi_irte hook to setup posted interrupt") Cc: stable@vger.kernel.org Signed-off-by: Sean Christopherson <seanjc@google.com> Message-ID: <20250404193923.1413163-3-seanjc@google.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
a22fe6f843
commit
b5de7ac74f
+31
-27
@@ -896,6 +896,7 @@ int avic_pi_update_irte(struct kvm *kvm, unsigned int host_irq,
|
|||||||
{
|
{
|
||||||
struct kvm_kernel_irq_routing_entry *e;
|
struct kvm_kernel_irq_routing_entry *e;
|
||||||
struct kvm_irq_routing_table *irq_rt;
|
struct kvm_irq_routing_table *irq_rt;
|
||||||
|
bool enable_remapped_mode = true;
|
||||||
int idx, ret = 0;
|
int idx, ret = 0;
|
||||||
|
|
||||||
if (!kvm_arch_has_assigned_device(kvm) ||
|
if (!kvm_arch_has_assigned_device(kvm) ||
|
||||||
@@ -933,6 +934,8 @@ int avic_pi_update_irte(struct kvm *kvm, unsigned int host_irq,
|
|||||||
kvm_vcpu_apicv_active(&svm->vcpu)) {
|
kvm_vcpu_apicv_active(&svm->vcpu)) {
|
||||||
struct amd_iommu_pi_data pi;
|
struct amd_iommu_pi_data pi;
|
||||||
|
|
||||||
|
enable_remapped_mode = false;
|
||||||
|
|
||||||
/* Try to enable guest_mode in IRTE */
|
/* Try to enable guest_mode in IRTE */
|
||||||
pi.base = __sme_set(page_to_phys(svm->avic_backing_page) &
|
pi.base = __sme_set(page_to_phys(svm->avic_backing_page) &
|
||||||
AVIC_HPA_MASK);
|
AVIC_HPA_MASK);
|
||||||
@@ -951,33 +954,6 @@ int avic_pi_update_irte(struct kvm *kvm, unsigned int host_irq,
|
|||||||
*/
|
*/
|
||||||
if (!ret && pi.is_guest_mode)
|
if (!ret && pi.is_guest_mode)
|
||||||
svm_ir_list_add(svm, &pi);
|
svm_ir_list_add(svm, &pi);
|
||||||
} else {
|
|
||||||
/* Use legacy mode in IRTE */
|
|
||||||
struct amd_iommu_pi_data pi;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Here, pi is used to:
|
|
||||||
* - Tell IOMMU to use legacy mode for this interrupt.
|
|
||||||
* - Retrieve ga_tag of prior interrupt remapping data.
|
|
||||||
*/
|
|
||||||
pi.prev_ga_tag = 0;
|
|
||||||
pi.is_guest_mode = false;
|
|
||||||
ret = irq_set_vcpu_affinity(host_irq, &pi);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the posted interrupt was previously
|
|
||||||
* setup with the guest_mode by checking if the ga_tag
|
|
||||||
* was cached. If so, we need to clean up the per-vcpu
|
|
||||||
* ir_list.
|
|
||||||
*/
|
|
||||||
if (!ret && pi.prev_ga_tag) {
|
|
||||||
int id = AVIC_GATAG_TO_VCPUID(pi.prev_ga_tag);
|
|
||||||
struct kvm_vcpu *vcpu;
|
|
||||||
|
|
||||||
vcpu = kvm_get_vcpu_by_id(kvm, id);
|
|
||||||
if (vcpu)
|
|
||||||
svm_ir_list_del(to_svm(vcpu), &pi);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ret && svm) {
|
if (!ret && svm) {
|
||||||
@@ -993,6 +969,34 @@ int avic_pi_update_irte(struct kvm *kvm, unsigned int host_irq,
|
|||||||
}
|
}
|
||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
if (enable_remapped_mode) {
|
||||||
|
/* Use legacy mode in IRTE */
|
||||||
|
struct amd_iommu_pi_data pi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Here, pi is used to:
|
||||||
|
* - Tell IOMMU to use legacy mode for this interrupt.
|
||||||
|
* - Retrieve ga_tag of prior interrupt remapping data.
|
||||||
|
*/
|
||||||
|
pi.prev_ga_tag = 0;
|
||||||
|
pi.is_guest_mode = false;
|
||||||
|
ret = irq_set_vcpu_affinity(host_irq, &pi);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the posted interrupt was previously
|
||||||
|
* setup with the guest_mode by checking if the ga_tag
|
||||||
|
* was cached. If so, we need to clean up the per-vcpu
|
||||||
|
* ir_list.
|
||||||
|
*/
|
||||||
|
if (!ret && pi.prev_ga_tag) {
|
||||||
|
int id = AVIC_GATAG_TO_VCPUID(pi.prev_ga_tag);
|
||||||
|
struct kvm_vcpu *vcpu;
|
||||||
|
|
||||||
|
vcpu = kvm_get_vcpu_by_id(kvm, id);
|
||||||
|
if (vcpu)
|
||||||
|
svm_ir_list_del(to_svm(vcpu), &pi);
|
||||||
|
}
|
||||||
|
}
|
||||||
out:
|
out:
|
||||||
srcu_read_unlock(&kvm->irq_srcu, idx);
|
srcu_read_unlock(&kvm->irq_srcu, idx);
|
||||||
return ret;
|
return ret;
|
||||||
|
|||||||
@@ -274,6 +274,7 @@ int vmx_pi_update_irte(struct kvm *kvm, unsigned int host_irq,
|
|||||||
{
|
{
|
||||||
struct kvm_kernel_irq_routing_entry *e;
|
struct kvm_kernel_irq_routing_entry *e;
|
||||||
struct kvm_irq_routing_table *irq_rt;
|
struct kvm_irq_routing_table *irq_rt;
|
||||||
|
bool enable_remapped_mode = true;
|
||||||
struct kvm_lapic_irq irq;
|
struct kvm_lapic_irq irq;
|
||||||
struct kvm_vcpu *vcpu;
|
struct kvm_vcpu *vcpu;
|
||||||
struct vcpu_data vcpu_info;
|
struct vcpu_data vcpu_info;
|
||||||
@@ -312,21 +313,8 @@ int vmx_pi_update_irte(struct kvm *kvm, unsigned int host_irq,
|
|||||||
|
|
||||||
kvm_set_msi_irq(kvm, e, &irq);
|
kvm_set_msi_irq(kvm, e, &irq);
|
||||||
if (!kvm_intr_is_single_vcpu(kvm, &irq, &vcpu) ||
|
if (!kvm_intr_is_single_vcpu(kvm, &irq, &vcpu) ||
|
||||||
!kvm_irq_is_postable(&irq)) {
|
!kvm_irq_is_postable(&irq))
|
||||||
/*
|
|
||||||
* Make sure the IRTE is in remapped mode if
|
|
||||||
* we don't handle it in posted mode.
|
|
||||||
*/
|
|
||||||
ret = irq_set_vcpu_affinity(host_irq, NULL);
|
|
||||||
if (ret < 0) {
|
|
||||||
printk(KERN_INFO
|
|
||||||
"failed to back to remapped mode, irq: %u\n",
|
|
||||||
host_irq);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
vcpu_info.pi_desc_addr = __pa(vcpu_to_pi_desc(vcpu));
|
vcpu_info.pi_desc_addr = __pa(vcpu_to_pi_desc(vcpu));
|
||||||
vcpu_info.vector = irq.vector;
|
vcpu_info.vector = irq.vector;
|
||||||
@@ -334,11 +322,12 @@ int vmx_pi_update_irte(struct kvm *kvm, unsigned int host_irq,
|
|||||||
trace_kvm_pi_irte_update(host_irq, vcpu->vcpu_id, e->gsi,
|
trace_kvm_pi_irte_update(host_irq, vcpu->vcpu_id, e->gsi,
|
||||||
vcpu_info.vector, vcpu_info.pi_desc_addr, set);
|
vcpu_info.vector, vcpu_info.pi_desc_addr, set);
|
||||||
|
|
||||||
if (set)
|
if (!set)
|
||||||
ret = irq_set_vcpu_affinity(host_irq, &vcpu_info);
|
continue;
|
||||||
else
|
|
||||||
ret = irq_set_vcpu_affinity(host_irq, NULL);
|
|
||||||
|
|
||||||
|
enable_remapped_mode = false;
|
||||||
|
|
||||||
|
ret = irq_set_vcpu_affinity(host_irq, &vcpu_info);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
printk(KERN_INFO "%s: failed to update PI IRTE\n",
|
printk(KERN_INFO "%s: failed to update PI IRTE\n",
|
||||||
__func__);
|
__func__);
|
||||||
@@ -346,6 +335,9 @@ int vmx_pi_update_irte(struct kvm *kvm, unsigned int host_irq,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (enable_remapped_mode)
|
||||||
|
ret = irq_set_vcpu_affinity(host_irq, NULL);
|
||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
out:
|
out:
|
||||||
srcu_read_unlock(&kvm->irq_srcu, idx);
|
srcu_read_unlock(&kvm->irq_srcu, idx);
|
||||||
|
|||||||
Reference in New Issue
Block a user