Merge: KVM: kvm fixes for 2026-07-21

MR: https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-9/-/merge_requests/8407

KVM: kvm fixes for 2026-07-21

JIRA: https://redhat.atlassian.net/browse/RHEL-211279

JIRA: https://redhat.atlassian.net/browse/RHEL-213339

CVE: CVE-2025-40026

CVE: CVE-2026-63807

Commits:

```
KVM: x86/mmu: Ensure hugepage is in by slot before checking max mapping level
KVM: nVMX: Hide shadow VMCS right after VMCLEAR
KVM: x86: Check for invalid/obsolete root *after* making MMU pages available
KVM: nVMX: Add helper to put (unmap) vmcs12 pages
KVM: nVMX: Put vmcs12 pages if nested VM-Enter fails due to invalid guest state
KVM: x86: Don't (re)check L1 intercepts when completing userspace I/O
```

Signed-off-by: Aidan Wallace <awallace@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

Approved-by: Peter Xu <peterx@redhat.com>
Approved-by: Jay Shin <jaeshin@redhat.com>
Approved-by: CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com>

Merged-by: CKI GitLab Kmaint Pipeline Bot <26919896-cki-kmaint-pipeline-bot@users.noreply.gitlab.com>
This commit is contained in:
CKI KWF Bot
2026-08-11 16:53:10 +00:00
7 changed files with 71 additions and 45 deletions
+4 -5
View File
@@ -5107,12 +5107,11 @@ void init_decode_cache(struct x86_emulate_ctxt *ctxt)
ctxt->mem_read.end = 0;
}
int x86_emulate_insn(struct x86_emulate_ctxt *ctxt)
int x86_emulate_insn(struct x86_emulate_ctxt *ctxt, bool check_intercepts)
{
const struct x86_emulate_ops *ops = ctxt->ops;
int rc = X86EMUL_CONTINUE;
int saved_dst_type = ctxt->dst.type;
bool is_guest_mode = ctxt->ops->is_guest_mode(ctxt);
ctxt->mem_read.pos = 0;
@@ -5160,7 +5159,7 @@ int x86_emulate_insn(struct x86_emulate_ctxt *ctxt)
fetch_possible_mmx_operand(&ctxt->dst);
}
if (unlikely(is_guest_mode) && ctxt->intercept) {
if (unlikely(check_intercepts) && ctxt->intercept) {
rc = emulator_check_intercept(ctxt, ctxt->intercept,
X86_ICPT_PRE_EXCEPT);
if (rc != X86EMUL_CONTINUE)
@@ -5189,7 +5188,7 @@ int x86_emulate_insn(struct x86_emulate_ctxt *ctxt)
goto done;
}
if (unlikely(is_guest_mode) && (ctxt->d & Intercept)) {
if (unlikely(check_intercepts) && (ctxt->d & Intercept)) {
rc = emulator_check_intercept(ctxt, ctxt->intercept,
X86_ICPT_POST_EXCEPT);
if (rc != X86EMUL_CONTINUE)
@@ -5243,7 +5242,7 @@ int x86_emulate_insn(struct x86_emulate_ctxt *ctxt)
special_insn:
if (unlikely(is_guest_mode) && (ctxt->d & Intercept)) {
if (unlikely(check_intercepts) && (ctxt->d & Intercept)) {
rc = emulator_check_intercept(ctxt, ctxt->intercept,
X86_ICPT_POST_MEMACCESS);
if (rc != X86EMUL_CONTINUE)
+1 -2
View File
@@ -233,7 +233,6 @@ struct x86_emulate_ops {
void (*set_nmi_mask)(struct x86_emulate_ctxt *ctxt, bool masked);
bool (*is_smm)(struct x86_emulate_ctxt *ctxt);
bool (*is_guest_mode)(struct x86_emulate_ctxt *ctxt);
int (*leave_smm)(struct x86_emulate_ctxt *ctxt);
void (*triple_fault)(struct x86_emulate_ctxt *ctxt);
int (*set_xcr)(struct x86_emulate_ctxt *ctxt, u32 index, u64 xcr);
@@ -519,7 +518,7 @@ bool x86_page_table_writing_insn(struct x86_emulate_ctxt *ctxt);
#define EMULATION_RESTART 1
#define EMULATION_INTERCEPTED 2
void init_decode_cache(struct x86_emulate_ctxt *ctxt);
int x86_emulate_insn(struct x86_emulate_ctxt *ctxt);
int x86_emulate_insn(struct x86_emulate_ctxt *ctxt, bool check_intercepts);
int emulator_task_switch(struct x86_emulate_ctxt *ctxt,
u16 tss_selector, int idt_index, int reason,
bool has_error_code, u32 error_code);
+17 -10
View File
@@ -4573,16 +4573,17 @@ static int direct_page_fault(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault
if (r != RET_PF_CONTINUE)
return r;
r = RET_PF_RETRY;
write_lock(&vcpu->kvm->mmu_lock);
if (is_page_fault_stale(vcpu, fault))
goto out_unlock;
r = make_mmu_pages_available(vcpu);
if (r)
goto out_unlock;
if (is_page_fault_stale(vcpu, fault)) {
r = RET_PF_RETRY;
goto out_unlock;
}
r = direct_map(vcpu, fault);
out_unlock:
@@ -6916,13 +6917,19 @@ restart:
sp = sptep_to_sp(sptep);
/*
* We cannot do huge page mapping for indirect shadow pages,
* which are found on the last rmap (level = 1) when not using
* tdp; such shadow pages are synced with the page table in
* the guest, and the guest page table is using 4K page size
* mapping if the indirect sp has level = 1.
* Direct shadow page can be replaced by a hugepage if the host
* mapping level allows it and the memslot maps all of the host
* hugepage. Note! If the memslot maps only part of the
* hugepage, sp->gfn may be below slot->base_gfn, and querying
* the max mapping level would cause an out-of-bounds lpage_info
* access. So the gfn bounds check *must* be done first.
*
* Indirect shadow pages are created when the guest page tables
* are using 4K pages. Since the host mapping is always
* constrained by the page size in the guest, indirect shadow
* pages are never collapsible.
*/
if (sp->role.direct &&
if (sp->role.direct && is_gfn_in_memslot(slot, sp->gfn) &&
sp->role.level < kvm_mmu_max_mapping_level(kvm, slot, sp->gfn,
PG_LEVEL_NUM)) {
kvm_zap_one_rmap_spte(kvm, rmap_head, sptep);
+6 -4
View File
@@ -835,15 +835,17 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault
walker.pte_access &= ~ACC_EXEC_MASK;
}
r = RET_PF_RETRY;
write_lock(&vcpu->kvm->mmu_lock);
if (is_page_fault_stale(vcpu, fault))
goto out_unlock;
r = make_mmu_pages_available(vcpu);
if (r)
goto out_unlock;
if (is_page_fault_stale(vcpu, fault)) {
r = RET_PF_RETRY;
goto out_unlock;
}
r = FNAME(fetch)(vcpu, fault, &walker);
out_unlock:
+29 -16
View File
@@ -317,6 +317,21 @@ static void vmx_switch_vmcs(struct kvm_vcpu *vcpu, struct loaded_vmcs *vmcs)
vcpu->arch.regs_dirty = 0;
}
static void nested_put_vmcs12_pages(struct kvm_vcpu *vcpu)
{
struct vcpu_vmx *vmx = to_vmx(vcpu);
/*
* Unpin physical memory we referred to in the vmcs02. The APIC access
* page's backing page (yeah, confusing) shouldn't actually be accessed,
* and if it is written, the contents are irrelevant.
*/
kvm_vcpu_unmap(vcpu, &vmx->nested.apic_access_page_map, false);
kvm_vcpu_unmap(vcpu, &vmx->nested.virtual_apic_map, true);
kvm_vcpu_unmap(vcpu, &vmx->nested.pi_desc_map, true);
vmx->nested.pi_desc = NULL;
}
/*
* Free whatever needs to be freed from vmx->nested when L1 goes down, or
* just stops using VMX.
@@ -324,6 +339,7 @@ static void vmx_switch_vmcs(struct kvm_vcpu *vcpu, struct loaded_vmcs *vmcs)
static void free_nested(struct kvm_vcpu *vcpu)
{
struct vcpu_vmx *vmx = to_vmx(vcpu);
struct vmcs *shadow_vmcs;
if (WARN_ON_ONCE(vmx->loaded_vmcs != &vmx->vmcs01))
vmx_switch_vmcs(vcpu, &vmx->vmcs01);
@@ -341,23 +357,22 @@ static void free_nested(struct kvm_vcpu *vcpu)
vmx->nested.current_vmptr = INVALID_GPA;
if (enable_shadow_vmcs) {
vmx_disable_shadow_vmcs(vmx);
vmcs_clear(vmx->vmcs01.shadow_vmcs);
free_vmcs(vmx->vmcs01.shadow_vmcs);
/*
* Keep the pointer visible until after VMCLEAR, so migration
* can clear an active shadow VMCS on the old CPU.
*/
shadow_vmcs = vmx->vmcs01.shadow_vmcs;
vmcs_clear(shadow_vmcs);
vmx->vmcs01.shadow_vmcs = NULL;
free_vmcs(shadow_vmcs);
}
kfree(vmx->nested.cached_vmcs12);
vmx->nested.cached_vmcs12 = NULL;
kfree(vmx->nested.cached_shadow_vmcs12);
vmx->nested.cached_shadow_vmcs12 = NULL;
/*
* Unpin physical memory we referred to in the vmcs02. The APIC access
* page's backing page (yeah, confusing) shouldn't actually be accessed,
* and if it is written, the contents are irrelevant.
*/
kvm_vcpu_unmap(vcpu, &vmx->nested.apic_access_page_map, false);
kvm_vcpu_unmap(vcpu, &vmx->nested.virtual_apic_map, true);
kvm_vcpu_unmap(vcpu, &vmx->nested.pi_desc_map, true);
vmx->nested.pi_desc = NULL;
nested_put_vmcs12_pages(vcpu);
kvm_mmu_free_roots(vcpu->kvm, &vcpu->arch.guest_mmu, KVM_MMU_ROOTS_ALL);
@@ -3607,6 +3622,8 @@ vmentry_fail_vmexit:
if (!from_vmentry)
return NVMX_VMENTRY_VMEXIT;
nested_put_vmcs12_pages(vcpu);
load_vmcs12_host_state(vcpu, vmcs12);
vmcs12->vm_exit_reason = exit_reason.full;
if (enable_shadow_vmcs || nested_vmx_is_evmptr12_valid(vmx))
@@ -4959,11 +4976,7 @@ void __nested_vmx_vmexit(struct kvm_vcpu *vcpu, u32 vm_exit_reason,
vmx_update_cpu_dirty_logging(vcpu);
}
/* Unpin physical memory we referred to in vmcs02 */
kvm_vcpu_unmap(vcpu, &vmx->nested.apic_access_page_map, false);
kvm_vcpu_unmap(vcpu, &vmx->nested.virtual_apic_map, true);
kvm_vcpu_unmap(vcpu, &vmx->nested.pi_desc_map, true);
vmx->nested.pi_desc = NULL;
nested_put_vmcs12_pages(vcpu);
if (vmx->nested.reload_vmcs01_apic_access_page) {
vmx->nested.reload_vmcs01_apic_access_page = false;
+8 -7
View File
@@ -8621,11 +8621,6 @@ static bool emulator_is_smm(struct x86_emulate_ctxt *ctxt)
return is_smm(emul_to_vcpu(ctxt));
}
static bool emulator_is_guest_mode(struct x86_emulate_ctxt *ctxt)
{
return is_guest_mode(emul_to_vcpu(ctxt));
}
#ifndef CONFIG_KVM_SMM
static int emulator_leave_smm(struct x86_emulate_ctxt *ctxt)
{
@@ -8709,7 +8704,6 @@ static const struct x86_emulate_ops emulate_ops = {
.guest_cpuid_is_intel_compatible = emulator_guest_cpuid_is_intel_compatible,
.set_nmi_mask = emulator_set_nmi_mask,
.is_smm = emulator_is_smm,
.is_guest_mode = emulator_is_guest_mode,
.leave_smm = emulator_leave_smm,
.triple_fault = emulator_triple_fault,
.set_xcr = emulator_set_xcr,
@@ -9327,7 +9321,14 @@ restart:
ctxt->exception.address = 0;
}
r = x86_emulate_insn(ctxt);
/*
* Check L1's instruction intercepts when emulating instructions for
* L2, unless KVM is re-emulating a previously decoded instruction,
* e.g. to complete userspace I/O, in which case KVM has already
* checked the intercepts.
*/
r = x86_emulate_insn(ctxt, is_guest_mode(vcpu) &&
!(emulation_type & EMULTYPE_NO_DECODE));
if (r == EMULATION_INTERCEPTED)
return 1;
+6 -1
View File
@@ -1735,6 +1735,11 @@ int kvm_request_irq_source_id(struct kvm *kvm);
void kvm_free_irq_source_id(struct kvm *kvm, int irq_source_id);
bool kvm_arch_irqfd_allowed(struct kvm *kvm, struct kvm_irqfd *args);
static inline bool is_gfn_in_memslot(const struct kvm_memory_slot *slot, gfn_t gfn)
{
return gfn >= slot->base_gfn && gfn < slot->base_gfn + slot->npages;
}
/*
* Returns a pointer to the memslot if it contains gfn.
* Otherwise returns NULL.
@@ -1745,7 +1750,7 @@ try_get_memslot(struct kvm_memory_slot *slot, gfn_t gfn)
if (!slot)
return NULL;
if (gfn >= slot->base_gfn && gfn < slot->base_gfn + slot->npages)
if (is_gfn_in_memslot(slot, gfn))
return slot;
else
return NULL;