Three bug fixes for x86:

* Check that nEPT/nNPT is enabled in slow flush hypercalls.  If it is
   not, the hypercalls can be processed as usual even while running a
   nested guest.
 
 * Fix shadow paging use-after-free due to page tables changing outside
   execution of the guest.  A bug that is 16 years old and stems from an
   imprecision in the very first KVM series.
 
 * Scan IRR whenever PID.ON is true, even if PIR is empty, which avoids
   a somewhat rare WARN.
 -----BEGIN PGP SIGNATURE-----
 
 iQFIBAABCgAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmn3sQoUHHBib256aW5p
 QHJlZGhhdC5jb20ACgkQv/vSX3jHroNMdQf/WuwNbJCb2bVWCoNeQUWvtxrlmZ5p
 IqQrnEACYOkn20eWDrXrAiEqPoIj0fPTYSewU9mYoC5DhyGFXzB5TEwMMHUn9kNn
 QVBQS/IlfuhO5BGrov4dbdmzUmXaTuUe29tjoOnr1IGNapf6naA9+m03u5/TaZcd
 FtS28EPy5Z2ZRm3KiKrZahsMMVr9ZXI3xfiEFVht9lDwiYpZquxhH2bju3QnBQ71
 0x02ZxMKbqwi5uJLCGmF62AacCSqag1+eNs05WFPThL5RNNkwpac1lceOetYFm9/
 y+iqOfev4SYeL3rACyQ/nStpgdkhpL2cbkPISRvjQ0WKVVawk+IAzNzvsQ==
 =sGxH
 -----END PGP SIGNATURE-----

Merge tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm

Pull kvm fixes from Paolo Bonzini:
 "Three bug fixes for x86:

   - Check that nEPT/nNPT is enabled in slow flush hypercalls. If it is
     not, the hypercalls can be processed as usual even while running a
     nested guest

   - Fix shadow paging use-after-free due to page tables changing
     outside execution of the guest. A bug that is 16 years old and
     stems from an imprecision in the very first KVM series

   - Scan IRR whenever PID.ON is true, even if PIR is empty, which
     avoids a somewhat rare WARN"

* tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm:
  KVM: x86: Fix shadow paging use-after-free due to unexpected GFN
  KVM: x86: Fix misleading variable names and add more comments for PIR=>IRR flow
  KVM: x86: Do IRR scan in __kvm_apic_update_irr even if PIR is empty
  KVM: x86: check for nEPT/nNPT in slow flush hypercalls
This commit is contained in:
Linus Torvalds 2026-05-03 15:25:47 -07:00
commit 6d35786de2
4 changed files with 60 additions and 41 deletions

View File

@ -2040,7 +2040,7 @@ static u64 kvm_hv_flush_tlb(struct kvm_vcpu *vcpu, struct kvm_hv_hcall *hc)
* flush). Translate the address here so the memory can be uniformly
* read with kvm_read_guest().
*/
if (!hc->fast && is_guest_mode(vcpu)) {
if (!hc->fast && mmu_is_nested(vcpu)) {
hc->ingpa = translate_nested_gpa(vcpu, hc->ingpa, 0, NULL);
if (unlikely(hc->ingpa == INVALID_GPA))
return HV_STATUS_INVALID_HYPERCALL_INPUT;

View File

@ -667,13 +667,15 @@ bool __kvm_apic_update_irr(unsigned long *pir, void *regs, int *max_irr)
u32 *__pir = (void *)pir_vals;
u32 i, vec;
u32 irr_val, prev_irr_val;
int max_updated_irr;
int max_new_irr;
max_updated_irr = -1;
*max_irr = -1;
if (!pi_harvest_pir(pir, pir_vals))
if (!pi_harvest_pir(pir, pir_vals)) {
*max_irr = apic_find_highest_vector(regs + APIC_IRR);
return false;
}
max_new_irr = -1;
*max_irr = -1;
for (i = vec = 0; i <= 7; i++, vec += 32) {
u32 *p_irr = (u32 *)(regs + APIC_IRR + i * 0x10);
@ -688,25 +690,25 @@ bool __kvm_apic_update_irr(unsigned long *pir, void *regs, int *max_irr)
!try_cmpxchg(p_irr, &prev_irr_val, irr_val));
if (prev_irr_val != irr_val)
max_updated_irr = __fls(irr_val ^ prev_irr_val) + vec;
max_new_irr = __fls(irr_val ^ prev_irr_val) + vec;
}
if (irr_val)
*max_irr = __fls(irr_val) + vec;
}
return ((max_updated_irr != -1) &&
(max_updated_irr == *max_irr));
return max_new_irr != -1 && max_new_irr == *max_irr;
}
EXPORT_SYMBOL_FOR_KVM_INTERNAL(__kvm_apic_update_irr);
bool kvm_apic_update_irr(struct kvm_vcpu *vcpu, unsigned long *pir, int *max_irr)
{
struct kvm_lapic *apic = vcpu->arch.apic;
bool irr_updated = __kvm_apic_update_irr(pir, apic->regs, max_irr);
bool max_irr_is_from_pir;
if (unlikely(!apic->apicv_active && irr_updated))
max_irr_is_from_pir = __kvm_apic_update_irr(pir, apic->regs, max_irr);
if (unlikely(!apic->apicv_active && max_irr_is_from_pir))
apic->irr_pending = true;
return irr_updated;
return max_irr_is_from_pir;
}
EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_apic_update_irr);

View File

@ -182,6 +182,8 @@ static struct kmem_cache *pte_list_desc_cache;
struct kmem_cache *mmu_page_header_cache;
static void mmu_spte_set(u64 *sptep, u64 spte);
static int mmu_page_zap_pte(struct kvm *kvm, struct kvm_mmu_page *sp,
u64 *spte, struct list_head *invalid_list);
struct kvm_mmu_role_regs {
const unsigned long cr0;
@ -1287,19 +1289,6 @@ static void drop_spte(struct kvm *kvm, u64 *sptep)
rmap_remove(kvm, sptep);
}
static void drop_large_spte(struct kvm *kvm, u64 *sptep, bool flush)
{
struct kvm_mmu_page *sp;
sp = sptep_to_sp(sptep);
WARN_ON_ONCE(sp->role.level == PG_LEVEL_4K);
drop_spte(kvm, sptep);
if (flush)
kvm_flush_remote_tlbs_sptep(kvm, sptep);
}
/*
* Write-protect on the specified @sptep, @pt_protect indicates whether
* spte write-protection is caused by protecting shadow page table.
@ -2466,7 +2455,8 @@ static struct kvm_mmu_page *kvm_mmu_get_child_sp(struct kvm_vcpu *vcpu,
{
union kvm_mmu_page_role role;
if (is_shadow_present_pte(*sptep) && !is_large_pte(*sptep))
if (is_shadow_present_pte(*sptep) && !is_large_pte(*sptep) &&
spte_to_child_sp(*sptep) && spte_to_child_sp(*sptep)->gfn == gfn)
return ERR_PTR(-EEXIST);
role = kvm_mmu_child_role(sptep, direct, access);
@ -2544,13 +2534,16 @@ static void __link_shadow_page(struct kvm *kvm,
BUILD_BUG_ON(VMX_EPT_WRITABLE_MASK != PT_WRITABLE_MASK);
/*
* If an SPTE is present already, it must be a leaf and therefore
* a large one. Drop it, and flush the TLB if needed, before
* installing sp.
*/
if (is_shadow_present_pte(*sptep))
drop_large_spte(kvm, sptep, flush);
if (is_shadow_present_pte(*sptep)) {
struct kvm_mmu_page *parent_sp;
LIST_HEAD(invalid_list);
parent_sp = sptep_to_sp(sptep);
WARN_ON_ONCE(parent_sp->role.level == PG_LEVEL_4K);
mmu_page_zap_pte(kvm, parent_sp, sptep, &invalid_list);
kvm_mmu_remote_flush_or_zap(kvm, &invalid_list, true);
}
spte = make_nonleaf_spte(sp->spt, sp_ad_disabled(sp));

View File

@ -7029,8 +7029,8 @@ static void vmx_set_rvi(int vector)
int vmx_sync_pir_to_irr(struct kvm_vcpu *vcpu)
{
struct vcpu_vt *vt = to_vt(vcpu);
bool max_irr_is_from_pir;
int max_irr;
bool got_posted_interrupt;
if (KVM_BUG_ON(!enable_apicv, vcpu->kvm))
return -EIO;
@ -7042,17 +7042,22 @@ int vmx_sync_pir_to_irr(struct kvm_vcpu *vcpu)
* But on x86 this is just a compiler barrier anyway.
*/
smp_mb__after_atomic();
got_posted_interrupt =
kvm_apic_update_irr(vcpu, vt->pi_desc.pir, &max_irr);
max_irr_is_from_pir = kvm_apic_update_irr(vcpu, vt->pi_desc.pir,
&max_irr);
} else {
max_irr = kvm_lapic_find_highest_irr(vcpu);
got_posted_interrupt = false;
max_irr_is_from_pir = false;
}
/*
* Newly recognized interrupts are injected via either virtual interrupt
* delivery (RVI) or KVM_REQ_EVENT. Virtual interrupt delivery is
* disabled in two cases:
* If APICv is enabled and L2 is not active, then update the Requesting
* Virtual Interrupt (RVI) portion of vmcs01.GUEST_INTR_STATUS with the
* highest priority IRR to deliver the IRQ via Virtual Interrupt
* Delivery. Note, this is required even if the highest priority IRQ
* was already pending in the IRR, as RVI isn't updated in lockstep with
* the IRR (unlike apic->irr_pending).
*
* For the cases where Virtual Interrupt Delivery can't be used:
*
* 1) If L2 is running and the vCPU has a new pending interrupt. If L1
* wants to exit on interrupts, KVM_REQ_EVENT is needed to synthesize a
@ -7063,10 +7068,29 @@ int vmx_sync_pir_to_irr(struct kvm_vcpu *vcpu)
* 2) If APICv is disabled for this vCPU, assigned devices may still
* attempt to post interrupts. The posted interrupt vector will cause
* a VM-Exit and the subsequent entry will call sync_pir_to_irr.
*
* In both cases, set KVM_REQ_EVENT if and only if the highest priority
* pending IRQ came from the PIR, as setting KVM_REQ_EVENT if any IRQ
* is pending may put the vCPU into an infinite loop, e.g. if the IRQ
* is blocked, then it will stay pending until an IRQ window is opened.
*
* Note! It's possible that one or more IRQs were moved from the PIR
* to the IRR _without_ max_irr_is_from_pir being true! I.e. if there
* was a higher priority IRQ already pending in the IRR. Not setting
* KVM_REQ_EVENT in this case is intentional and safe. If APICv is
* inactive, or L2 is running with exit-on-interrupt off (in vmcs12),
* i.e. without nested virtual interrupt delivery, then there's no need
* to request an IRQ window as the lower priority IRQ only needs to be
* delivered when the higher priority IRQ is dismissed from the ISR,
* i.e. on the next EOI, and EOIs are always intercepted if APICv is
* disabled or if L2 is running without nested VID. If L2 is running
* exit-on-interrupt on (in vmcs12), then the higher priority IRQ will
* trigger a nested VM-Exit, at which point KVM will re-evaluate L1's
* pending IRQs.
*/
if (!is_guest_mode(vcpu) && kvm_vcpu_apicv_active(vcpu))
vmx_set_rvi(max_irr);
else if (got_posted_interrupt)
else if (max_irr_is_from_pir)
kvm_make_request(KVM_REQ_EVENT, vcpu);
return max_irr;