aboutsummaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorPaolo Bonzini <pbonzini@redhat.com>2026-10-09 17:49:26 +0200
committerPaolo Bonzini <pbonzini@redhat.com>2026-10-09 17:49:26 +0200
commitbb44efdb084a0fd600b64f2aaed5d932caa0c944 (patch)
tree87f0e381edf643cc2db4a37db737c7793f15f1ac /arch
parenta90ee4305c4a5df72c11b31dacfdc76e00fcf78a (diff)
parentb3e6212dea2e4328a841ceb37f377583176dc735 (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.c22
-rw-r--r--arch/x86/kvm/svm/sev.c2
-rw-r--r--arch/x86/kvm/svm/svm.c6
-rw-r--r--arch/x86/kvm/svm/svm.h1
-rw-r--r--arch/x86/kvm/x86.h4
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;
}
/*