From: sashiko-bot@kernel.org
To: "Sean Christopherson" <seanjc@google.com>
Cc: kvm@vger.kernel.org
Subject: Re: [PATCH v2 4/6] KVM: x86: Recompute EFER.LMA instead of inheriting the current value on EFER writes
Date: Tue, 27 Oct 2026 23:17:00 +0000 [thread overview]
Message-ID: <sashiko-outbox-187201@kernel.org> (raw)
In-Reply-To: <20261027224914.1574319-5-seanjc@google.com>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] kvm: x86: guest- and userspace-reachable WARN_ON_ONCE() in set_efer()
- [High] kvm: x86: missing MMU context reset in set_efer() when EFER_LMA changes
- [High] KVM: nSVM: guest-reachable WARN_ON_ONCE in set_efer() due to stale EFER.LMA
--
commit d17bdc446bf3834407b9fa79da8f4a4f619465b7
Author: Sean Christopherson <seanjc@google.com>
KVM: x86: Recompute EFER.LMA instead of inheriting the current value on EFER writes
This commit recomputes EFER.LMA from EFER.LME, CR0.PG, and CR4.PAE on bare
writes to MSR_EFER instead of inheriting the existing EFER.LMA bit from
vcpu->arch.efer, and adds a WARN_ON_ONCE() if EFER.LMA changes.
> diff --git a/arch/x86/kvm/msrs.c b/arch/x86/kvm/msrs.c
> index c3cb1575fe66e..c55402970a345 100644
> --- a/arch/x86/kvm/msrs.c
> +++ b/arch/x86/kvm/msrs.c
> @@ -638,8 +638,17 @@ static int set_efer(struct kvm_vcpu *vcpu, u64 efer)
> (vcpu->arch.efer & EFER_SVME) && !(efer & EFER_SVME))
> return 1;
>
> - efer &= ~EFER_LMA;
> - efer |= vcpu->arch.efer & EFER_LMA;
> + if ((efer & EFER_LME) && is_paging(vcpu) && is_pae(vcpu))
> + efer |= EFER_LMA;
> + else
> + efer &= ~EFER_LMA;
> +
> + /*
> + * Toggling EFER.LMA on a bare write to EFER should be impossible, as
> + * it means KVM allowed architecturally invalid state at some point in
> + * the past, i.e. allowed EFER.LMA=1 && (!CR0.PG || !CR4.PAE).
> + */
> + WARN_ON_ONCE((efer & EFER_LMA) != (vcpu->arch.efer & EFER_LMA));
[Severity: High]
Can an unprivileged guest or host userspace trigger this WARN_ON_ONCE() in
arch/x86/kvm/msrs.c:set_efer() by leaving vcpu->arch.efer with a stale
EFER_LMA across nested SVM transitions?
On AMD SVM, VMRUN ignores EFER.LMA in the VMCB save area and computes it
from (EFER.LME && CR0.PG), so arch/x86/kvm/svm/nested.c:nested_vmcb_check_save()
does not require save->efer's EFER_LMA to match (EFER.LME && CR0.PG).
In arch/x86/kvm/svm/nested.c:nested_vmcb02_prepare_save(), KVM only clears
EFER_LMA when EFER_LME is 0, and relies on svm_set_cr0() to update EFER_LMA
when EFER_LME is 1:
arch/x86/kvm/svm/nested.c:nested_vmcb02_prepare_save() {
/* SVM ignores EFER.LMA if EFER.LME=0 (instead of failing VMRUN). */
if (!(svm->nested.save.efer & EFER_LME))
svm->nested.save.efer &= ~EFER_LMA;
svm_set_efer(vcpu, svm->nested.save.efer);
svm_set_cr0(vcpu, svm->nested.save.cr0);
svm_set_cr4(vcpu, svm->nested.save.cr4);
}
However, arch/x86/kvm/svm/svm.c:svm_set_cr0() only updates EFER_LMA when
CR0.PG changes relative to the previous vcpu->arch.cr0 (is_paging(vcpu)):
arch/x86/kvm/svm/svm.c:svm_set_cr0() {
if (vcpu->arch.efer & EFER_LME) {
if (!is_paging(vcpu) && (cr0 & X86_CR0_PG)) {
vcpu->arch.efer |= EFER_LMA;
if (!vcpu->arch.guest_state_protected)
svm->vmcb->save.efer |= EFER_LMA | EFER_LME;
}
if (is_paging(vcpu) && !(cr0 & X86_CR0_PG)) {
vcpu->arch.efer &= ~EFER_LMA;
if (!vcpu->arch.guest_state_protected)
svm->vmcb->save.efer &= ~(EFER_LMA | EFER_LME);
}
}
}
This allows vcpu->arch.efer's EFER_LMA to become out of sync with
(EFER.LME && CR0.PG && CR4.PAE) in several ways:
1. Guest VMRUN (nested_svm_vmrun() -> enter_svm_guest_mode() ->
nested_vmcb02_prepare_save()): L1 must have CR0.PG=1 to execute VMRUN, so
is_paging(vcpu) is already true when nested_vmcb02_prepare_save() runs. If
L1 sets vmcb12->save with EFER_LME=1, EFER_LMA=0, CR0.PG=1, CR0.PE=1, and
CR4.PAE=1, svm_set_efer() loads EFER_LMA=0 into vcpu->arch.efer (and also
clears EFER_LME in vmcb02->save.efer when !npt_enabled), and svm_set_cr0()
sees !is_paging(vcpu) == false and never sets EFER_LMA. L2 is left with
EFER_LME=1, EFER_LMA=0, CR0.PG=1, CR4.PAE=1 (which also truncates L2's
64-bit RSP and RIP to 32 bits and configures the MMU for 32-bit PAE), and
a subsequent WRMSR(MSR_EFER) in L2 (or KVM_SET_MSRS from userspace)
computes EFER_LMA=1 and trips the WARN_ON_ONCE() in set_efer().
2. Guest RSM into L2 (emulator_leave_smm() -> svm_leave_smm() ->
enter_svm_guest_mode() -> nested_vmcb02_prepare_save() ->
rsm_load_state_64()): emulator_leave_smm() clears CR0.PG before calling
svm_leave_smm(), so is_paging(vcpu) is false. If vmcb12->save in guest
memory has EFER_LME=1, EFER_LMA=1, and CR0.PG=0, nested_vmcb02_prepare_save()
does not clear EFER_LMA and svm_set_cr0() sees is_paging(vcpu) == false and
leaves EFER_LMA=1 in vcpu->arch.efer while CR0.PG=0. Immediately after
svm_leave_smm() returns, arch/x86/kvm/smm.c:rsm_load_state_64() calls
__kvm_emulate_msr_write(vcpu, MSR_EFER, smstate->efer & ~EFER_LMA) ->
set_efer(), which computes EFER_LMA=0 and triggers the WARN_ON_ONCE().
3. KVM_SET_NESTED_STATE or SMM HSAVE restoration followed by nested #VMEXIT
(svm_set_nested_state() / svm_leave_smm() -> nested_svm_vmexit()):
arch/x86/kvm/svm/nested.c:svm_set_nested_state() requires CR0.PG=1 in L1's
saved host state (vmcb01->save) and rejects (EFER_LMA && !EFER_LME), but
allows EFER_LME=1 with EFER_LMA=0:
arch/x86/kvm/svm/nested.c:svm_set_nested_state() {
if (!(save->cr0 & X86_CR0_PG) ||
!(save->cr0 & X86_CR0_PE) ||
(save->rflags & X86_EFLAGS_VM) ||
((save->efer & EFER_LMA) && !(save->efer & EFER_LME)) ||
!nested_vmcb_check_save(vcpu, &save_cached, false))
goto out_free;
}
When exiting from a paged L2 (is_paging(vcpu) == true),
arch/x86/kvm/svm/nested.c:nested_svm_vmexit() calls
svm_set_efer(vcpu, vmcb01->save.efer) followed by
svm_set_cr0(vcpu, vmcb01->save.cr0 | X86_CR0_PE), leaving L1 with
EFER_LME=1, EFER_LMA=0, CR0.PG=1, CR4.PAE=1 and triggering the
WARN_ON_ONCE() on the next MSR_EFER write.
4. SEV-SNP AP creation (sev_snp_ap_creation() ->
KVM_REQ_UPDATE_PROTECTED_GUEST_STATE -> kvm_vcpu_reset()): svm_set_cr0()
sets EFER_LMA without checking is_pae(vcpu) while vcpu->arch.cr4 still has
CR4.PAE=0, allowing a subsequent trapped EFER write via efer_trap() to
trigger the WARN_ON_ONCE().
>
> r = kvm_x86_call(set_efer)(vcpu, efer);
> if (r) {
[Severity: High]
When set_efer() toggles EFER_LMA to restore architectural consistency,
doesn't it also need to reset the MMU context if EFER_LMA changed?
At the end of arch/x86/kvm/msrs.c:set_efer(), KVM only calls
kvm_mmu_reset_context(vcpu) when bits in KVM_MMU_EFER_ROLE_BITS change:
arch/x86/kvm/msrs.c:set_efer() {
...
if ((efer ^ old_efer) & KVM_MMU_EFER_ROLE_BITS)
kvm_mmu_reset_context(vcpu);
}
However, KVM_MMU_EFER_ROLE_BITS in arch/x86/kvm/mmu.h is defined as
(EFER_LME | EFER_NX) and does not include EFER_LMA, even though
arch/x86/kvm/mmu/mmu.c:kvm_calc_cpu_role() computes role.base.level,
role.ext.efer_lma, role.ext.cr4_pke, and role.ext.cr4_la57 from
____is_efer_lma(regs).
Because set_efer() rejects any write that modifies EFER_LME while
is_paging(vcpu) is true, ((efer ^ old_efer) & EFER_LME) is always 0 when
paging is enabled. If EFER_LMA is toggled by set_efer() while EFER_NX is
unchanged, ((efer ^ old_efer) & KVM_MMU_EFER_ROLE_BITS) evaluates to 0 and
kvm_mmu_reset_context(vcpu) is skipped, leaving the vCPU's MMU role and root
level stale relative to the updated vcpu->arch.efer.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261027224914.1574319-1-seanjc@google.com?part=4
next prev parent reply other threads:[~2026-10-27 23:17 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-27 22:49 [PATCH v2 0/6] KVM: x86: More EFER.LMA vs. CR4.PAE fixes Sean Christopherson
2026-10-27 22:49 ` [PATCH v2 1/6] Revert "KVM: x86: Skip EFER vs. guest CPUID checks for host-initiated writes" Sean Christopherson
2026-10-27 22:49 ` [PATCH v2 2/6] KVM: x86: Reject clearing EFER.SVME via KVM_SYNC_X86_SREGS if L2 is active Sean Christopherson
2026-10-28 0:08 ` Yosry Ahmed
2026-10-28 13:36 ` Sean Christopherson
2026-10-28 14:47 ` Yosry Ahmed
2026-10-27 22:49 ` [PATCH v2 3/6] KVM: nSVM: Synthesize #GP instead of SHUTDOWN if L2 attempts to clear EFER.SVME Sean Christopherson
2026-10-27 22:49 ` [PATCH v2 4/6] KVM: x86: Recompute EFER.LMA instead of inheriting the current value on EFER writes Sean Christopherson
2026-10-27 23:17 ` sashiko-bot [this message]
2026-10-28 13:39 ` Sean Christopherson
2026-10-27 22:49 ` [PATCH v2 5/6] KVM: selftests: Update nSVM EFER test to expect #GP when clearing SVME in L2 Sean Christopherson
2026-10-27 22:49 ` [PATCH v2 6/6] KVM: selftests: Expect failure when trying to set EFER that violates vCPU model Sean Christopherson
2026-10-27 22:52 ` sashiko-bot
2026-10-28 13:41 ` Sean Christopherson
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=sashiko-outbox-187201@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=kvm@vger.kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
--cc=seanjc@google.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.