mirror of
https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-9.git
synced 2026-09-09 00:08:12 +08:00
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:
@@ -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)
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user