Re: [PATCH v2 1/2] KVM: SVM: Triple fault L1 on unintercepted EFER.SVME clear by L2

From: Sean Christopherson

Date: Fri Feb 27 2026 - 19:43:29 EST


On Fri, Feb 27, 2026, Yosry Ahmed wrote:
> > > > @@ -216,6 +216,17 @@ int svm_set_efer(struct kvm_vcpu *vcpu, u64 efer)
> > > >
> > > > if ((old_efer & EFER_SVME) != (efer & EFER_SVME)) {
> > > > if (!(efer & EFER_SVME)) {
> > > > + /*
> > > > + * Architecturally, clearing EFER.SVME while a guest is
> > > > + * running yields undefined behavior, i.e. KVM can do
> > > > + * literally anything. Force the vCPU back into L1 as
> > > > + * that is the safest option for KVM, but synthesize a
> > > > + * triple fault (for L1!) so that KVM at least doesn't
> > > > + * run random L2 code in the context of L1.
> > > > + */
> > > > + if (is_guest_mode(vcpu))
> > > > + kvm_make_request(KVM_REQ_TRIPLE_FAULT, vcpu);
> > > > +
> > >
> > > Sigh, I think this is not correct in all cases:
> > >
> > > 1. If userspace restores a vCPU with EFER.SVME=0 to a vCPU with
> > > EFER.SVME=1 (e.g. restoring a vCPU running to a vCPU running L2).
> > > Typically KVM_SET_SREGS is done before KVM_SET_NESTED_STATE, so we may
> > > set EFER.SVME = 0 before leaving guest mode.
> > >
> > > 2. On vCPU reset, we clear EFER. Hmm, this one is seemingly okay tho,
> > > looking at kvm_vcpu_reset(), we leave nested first:
> > >
> > > /*
> > > * SVM doesn't unconditionally VM-Exit on INIT and SHUTDOWN, thus it's
> > > * possible to INIT the vCPU while L2 is active. Force the vCPU back
> > > * into L1 as EFER.SVME is cleared on INIT (along with all other EFER
> > > * bits), i.e. virtualization is disabled.
> > > */
> > > if (is_guest_mode(vcpu))
> > > kvm_leave_nested(vcpu);
> > >
> > > ...
> > >
> > > kvm_x86_call(set_efer)(vcpu, 0);
> > >
> > > So I think the only problematic case is (1). We can probably fix this by
> > > plumbing host_initiated through set_efer? This is getting more
> > > complicated than I would have liked..
> >
> > What if we instead hook WRMSR interception? A little fugly (well, more than a
> > little), but I think it would minimize the chances of a false-positive. The
> > biggest potential flaw I see is that this will incorrectly triple fault if KVM
> > synthesizes a #VMEXIT while emulating the WRMSR. But that really shouldn't
> > happen, because even a #GP=>#VMEXIT needs to be queued but not synthesized until
> > the emulation sequence completes (any other behavior would risk confusing KVM).
>
> What if we key off vcpu->wants_to_run?

That crossed my mind too.

> It's less protection against false positives from things like
> kvm_vcpu_reset() if it didn't leave nested before clearing EFER, but
> more protection against the #VMEXIT case you mentioned. Also should be
> much lower on the fugliness scale imo.

Yeah, I had pretty much the exact same thought process and assessment. I suggested
the WRMSR approach because I'm not sure how I feel about using wants_to_run for
functional behavior. But after realizing that hooking WRMSR won't handle RSM,
I'm solidly against my WRMSR idea.

Honestly, I'm leaning slightly towards dropping this patch entirely since it's
not a bug fix. But I'm definitely not completely against it either. So what if
we throw it in, but plan on reverting if there are any more problems (that aren't
obviously due to goofs elsewhere in KVM).

Is this what you were thinking?

---
arch/x86/kvm/svm/svm.c | 13 +++++++++++++
1 file changed, 13 insertions(+)

diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index 1b31b033d79b..3e48e9c1c955 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -216,6 +216,19 @@ int svm_set_efer(struct kvm_vcpu *vcpu, u64 efer)

if ((old_efer & EFER_SVME) != (efer & EFER_SVME)) {
if (!(efer & EFER_SVME)) {
+ /*
+ * Architecturally, clearing EFER.SVME while a guest is
+ * running yields undefined behavior, i.e. KVM can do
+ * literally anything. Force the vCPU back into L1 as
+ * that is the safest option for KVM, but synthesize a
+ * triple fault (for L1!) so that KVM at least doesn't
+ * run random L2 code in the context of L1. Do so if
+ * and only if the vCPU is actively running, e.g. to
+ * avoid positives if userspace is stuffing state.
+ */
+ if (is_guest_mode(vcpu) && vcpu->wants_to_run)
+ kvm_make_request(KVM_REQ_TRIPLE_FAULT, vcpu);
+
svm_leave_nested(vcpu);
/* #GP intercept is still needed for vmware backdoor */
if (!enable_vmware_backdoor)

base-commit: 95deaec3557dced322e2540bfa426e60e5373d46
--