diff options
| author | Paolo Bonzini <pbonzini@redhat.com> | 2026-10-09 17:49:26 +0200 |
|---|---|---|
| committer | Paolo Bonzini <pbonzini@redhat.com> | 2026-10-09 17:49:26 +0200 |
| commit | bb44efdb084a0fd600b64f2aaed5d932caa0c944 (patch) | |
| tree | 87f0e381edf643cc2db4a37db737c7793f15f1ac /arch | |
| parent | a90ee4305c4a5df72c11b31dacfdc76e00fcf78a (diff) | |
| parent | b3e6212dea2e4328a841ceb37f377583176dc735 (diff) | |
Merge tag 'kvm-x86-fixes-7.3-rc6' of https://github.com/kvm-x86/linux into HEAD
KVM x86 fixed for 7.3-rcN
- If creating a vCPU ultimately fails, clear its entry in the AVIC Physical ID
table used by hardware to perform IPI Virtualization and delivered posted
IRQs. I.e. plug the same use-after-free hole related to IPI virtualization
and posted IRQs that was fixed for APICv by commit b41f2ca6c0601 ("KVM: VMX:
Fix stale PID-pointer table entry left after vCPU free").
- Use set_page_dirty_lock() instead of set_page_dirty() when marking a page
dirty after writing back SNP firmware's desired CPUID state to userspace.
Using set_page_dirty() is unsafe if the page/folio isn't locked (as pointed
out by its function comment's big DANGER warning, which no one reads given
the number of bugs of this exact nature).
- When checking for an MMIO generation match, use the memslots for the current
vCPU context (non-SMM vs. SMM), as a misbehaving guest can coerce KVM into
incorrectly identifying a write as MMIO in order to bypass KVM's
write-tracking.
Diffstat (limited to 'arch')
| -rw-r--r-- | arch/x86/kvm/svm/avic.c | 22 | ||||
| -rw-r--r-- | arch/x86/kvm/svm/sev.c | 2 | ||||
| -rw-r--r-- | arch/x86/kvm/svm/svm.c | 6 | ||||
| -rw-r--r-- | arch/x86/kvm/svm/svm.h | 1 | ||||
| -rw-r--r-- | arch/x86/kvm/x86.h | 4 |
5 files changed, 25 insertions, 10 deletions
diff --git a/arch/x86/kvm/svm/avic.c b/arch/x86/kvm/svm/avic.c index 3b037e385523..96ca39c0045f 100644 --- a/arch/x86/kvm/svm/avic.c +++ b/arch/x86/kvm/svm/avic.c @@ -395,6 +395,11 @@ static phys_addr_t avic_get_backing_page_address(struct vcpu_svm *svm) return __sme_set(__pa(svm->vcpu.arch.apic->regs)); } +static bool avic_is_addressable_vcpu(struct kvm_vcpu *vcpu) +{ + return vcpu->vcpu_id <= __avic_get_max_physical_id(vcpu->kvm, NULL); +} + void avic_init_vmcb(struct vcpu_svm *svm, struct vmcb *vmcb) { struct kvm_svm *kvm_svm = to_kvm_svm(svm->vcpu.kvm); @@ -412,7 +417,6 @@ void avic_init_vmcb(struct vcpu_svm *svm, struct vmcb *vmcb) static int avic_init_backing_page(struct kvm_vcpu *vcpu) { - u32 max_id = x2avic_enabled ? x2avic_max_physical_id : AVIC_MAX_PHYSICAL_ID; struct kvm_svm *kvm_svm = to_kvm_svm(vcpu->kvm); struct vcpu_svm *svm = to_svm(vcpu); u32 id = vcpu->vcpu_id; @@ -425,7 +429,7 @@ static int avic_init_backing_page(struct kvm_vcpu *vcpu) * avic_vcpu_load() expects to be called if and only if the vCPU has * fully initialized AVIC. */ - if (id > max_id) { + if (!avic_is_addressable_vcpu(vcpu)) { kvm_set_apicv_inhibit(vcpu->kvm, APICV_INHIBIT_REASON_PHYSICAL_ID_TOO_BIG); vcpu->arch.apic->apicv_active = false; return 0; @@ -885,6 +889,14 @@ int avic_init_vcpu(struct vcpu_svm *svm) return ret; } +void avic_vcpu_free(struct kvm_vcpu *vcpu) +{ + struct kvm_svm *kvm_svm = to_kvm_svm(vcpu->kvm); + + if (kvm_svm->avic_physical_id_table && avic_is_addressable_vcpu(vcpu)) + WRITE_ONCE(kvm_svm->avic_physical_id_table[vcpu->vcpu_id], 0); +} + void avic_apicv_post_state_restore(struct kvm_vcpu *vcpu) { avic_handle_dfr_update(vcpu); @@ -1045,8 +1057,7 @@ static void __avic_vcpu_load(struct kvm_vcpu *vcpu, int cpu, if (WARN_ON(h_physical_id & ~AVIC_PHYSICAL_ID_ENTRY_HOST_PHYSICAL_ID_MASK)) return; - if (WARN_ON_ONCE(vcpu->vcpu_id * sizeof(entry) >= - PAGE_SIZE << avic_get_physical_id_table_order(vcpu->kvm))) + if (WARN_ON_ONCE(!avic_is_addressable_vcpu(vcpu))) return; /* @@ -1108,8 +1119,7 @@ static void __avic_vcpu_put(struct kvm_vcpu *vcpu, enum avic_vcpu_action action) lockdep_assert_preemption_disabled(); - if (WARN_ON_ONCE(vcpu->vcpu_id * sizeof(entry) >= - PAGE_SIZE << avic_get_physical_id_table_order(vcpu->kvm))) + if (WARN_ON_ONCE(!avic_is_addressable_vcpu(vcpu))) return; /* diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 0c1ebb16cec6..d24dbe633b11 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -2421,7 +2421,7 @@ static int sev_gmem_post_populate(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn, void *dst_vaddr = kmap_local_pfn(pfn); memcpy(src_vaddr, dst_vaddr, PAGE_SIZE); - set_page_dirty(src_page); + set_page_dirty_lock(src_page); kunmap_local(dst_vaddr); kunmap_local(src_vaddr); diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index fe0cba4731b2..c054803d4673 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -1328,7 +1328,7 @@ static int svm_vcpu_create(struct kvm_vcpu *vcpu) svm->msrpm = svm_vcpu_alloc_msrpm(); if (!svm->msrpm) { err = -ENOMEM; - goto error_free_sev; + goto error_free_avic; } svm->x2avic_msrs_intercepted = true; @@ -1342,6 +1342,8 @@ static int svm_vcpu_create(struct kvm_vcpu *vcpu) return 0; +error_free_avic: + avic_vcpu_free(vcpu); error_free_sev: sev_free_vcpu(vcpu); error_free_vmcb_page: @@ -1356,6 +1358,8 @@ static void svm_vcpu_free(struct kvm_vcpu *vcpu) WARN_ON_ONCE(!list_empty(&svm->ir_list)); + avic_vcpu_free(vcpu); + svm_leave_nested(vcpu); svm_free_nested(svm); diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h index 84f19026d3e8..29bde741874a 100644 --- a/arch/x86/kvm/svm/svm.h +++ b/arch/x86/kvm/svm/svm.h @@ -955,6 +955,7 @@ void avic_init_vmcb(struct vcpu_svm *svm, struct vmcb *vmcb); int avic_incomplete_ipi_interception(struct kvm_vcpu *vcpu); int avic_unaccelerated_access_interception(struct kvm_vcpu *vcpu); int avic_init_vcpu(struct vcpu_svm *svm); +void avic_vcpu_free(struct kvm_vcpu *vcpu); void avic_vcpu_load(struct kvm_vcpu *vcpu, int cpu); void avic_vcpu_put(struct kvm_vcpu *vcpu); void avic_apicv_post_state_restore(struct kvm_vcpu *vcpu); diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h index 0f5919b092e4..f72897ae56bc 100644 --- a/arch/x86/kvm/x86.h +++ b/arch/x86/kvm/x86.h @@ -250,7 +250,7 @@ static inline bool is_noncanonical_invlpg_address(u64 la, struct kvm_vcpu *vcpu) static inline void vcpu_cache_mmio_info(struct kvm_vcpu *vcpu, gva_t gva, gfn_t gfn, unsigned access) { - u64 gen = kvm_memslots(vcpu->kvm)->generation; + u64 gen = kvm_vcpu_memslots(vcpu)->generation; if (unlikely(gen & KVM_MEMSLOT_GEN_UPDATE_IN_PROGRESS)) return; @@ -267,7 +267,7 @@ static inline void vcpu_cache_mmio_info(struct kvm_vcpu *vcpu, static inline bool vcpu_match_mmio_gen(struct kvm_vcpu *vcpu) { - return vcpu->arch.mmio_gen == kvm_memslots(vcpu->kvm)->generation; + return vcpu->arch.mmio_gen == kvm_vcpu_memslots(vcpu)->generation; } /* |
