Re: [PATCH] KVM: nSVM: Reset the virtual VMLOAD/VMSAVE bit in vmcb02 on every nested VMRUN

From: Amirmohammad Eftekhar

Date: Sat Oct 31 2026 - 05:57:35 EST


On Fri, Oct 23, 2026, Yosry Ahmed wrote:
> Running the selftest under the probe on Zen 2/3/4/5, vmcb02 rows with both
> vls=1 and the VMLOAD/VMSAVE intercept set still exit 0x82/0x83, so the
> intercept is honored even with VLS enabled.
> Can you share a standalone reproducer?

Thanks for the cross-microarch data and for the script. You are right, and I
withdraw the intercept-drop claim. I reran this on a VLS-capable box
(Threadripper PRO 5965WX, Zen 3, kvm_amd vls=1) with your bpftrace unchanged,
and both your selftest and my own reproducer line up with your reading.

1. Your selftest reproduces your result here. Every VMRUN where vmcb02 carries
both the VLS bit and the VMLOAD/VMSAVE intercept exits 0x82/0x83, the
intercept wins:

vmcb02[vls=1 vmld_icpt=1] vmcb12[vls=1 vmld_icpt=1] VMLOAD -> 0x82
vmcb02[vls=1 vmld_icpt=1] vmcb12[vls=1 vmld_icpt=1] VMSAVE -> 0x83
vmcb02[vls=1 vmld_icpt=0] (intercept clear) VMLOAD virtualized, no 0x82/0x83

So VLS virtualizes only when the intercept is clear; with both bits set the
intercept is honored, exactly as you measured.

2. My reproducer does build the stale both-bits state, and it is still harmless.
Tracing --step vls-stale on the same box:

arm vmcb12(vls,icpt) vmcb02(vls,vmld_icpt) entry_rip exit
clean (0,1) (0,1) 0x1004000 0x82 VMLOAD intercepted
run1 (1,0) (1,0) 0x100400a 0x72 VLS on, VMLOAD virtualized, RIP advances
run2 (0,1) (1,1) <- stale VLS 0x100400f 0x72 resumes already on the CPUID

run2's vmcb02 is the exact case my patch was about: the VLS bit is left set
from run1 while KVM sets INTERCEPT_VMLOAD for run2. But its 0x72 is not a
dropped intercept. run1 virtualized the VMLOAD and advanced L2's RIP to the
CPUID, and my test runs run1 and run2 on one shared L2 context without
resetting RIP, so run2 resumes past the VMLOAD and never re-executes it. The
0x72 is that CPUID. Had VMLOAD actually run in run2 with both bits set, your
selftest shows it would have exited 0x82. My earlier Zen 4 "repro" was the
same RIP artifact, except that box could not enable VLS at all, so vmcb02
never even carried the bit and I misread the CPUID as a virtualized VMLOAD.

So the stale bit is real in the code, but it does not drop the intercept: the
hardware honors the intercept over VLS, as you said. The E3 / cross-layer
framing was wrong and I withdraw it.

What survives is only the hygiene you and Sean already pointed at:
virt_ext/misc_ctl2's VLS bit is OR-set in nested_vmcb02_prepare_control() and
never cleared, while misc_ctl is reset from vmcb01 a few lines above. Worth
folding into Sean's cleanup (initialize misc_ctl2 = 0 and un-invert the check to
match nested_vmcb02_recalc_intercepts()), as hygiene, not a guest-reachable fix.
The forward WARN in my original patch was oriented wrong; drop it.

Sorry for the noise on the security angle, and thanks for pushing back with the
measurements.

Thanks,
Amirmohammad