[PATCH v3 1/7] KVM: SVM: Drop RAX check for SVM instructions from the emulator

From: Yosry Ahmed

Date: Thu Mar 12 2026 - 20:11:07 EST


The check for legal GPA in RAX hardcodes a mask for 48 bits of physical
address width. This incorrectly injects a #GP for valid 52-bit GPAs.
However, instead of fixing the check, remove it completely as it is
unnecessary.

If RAX contains an illegal GPA, the CPU should inject #GP. If KVM
intercepts #GP, the emulator is only used for decoding the instruction.
Otherwise, if RAX is illegal from the guest's perspective but not the
host's (due to allow_smaller_maxphyaddr), then KVM should always
intercept the instructions (as NPT and VLS should both be disabled). The
interception path for VMRUN/VMSAVE/VMLOAD also does not invoke the
emulator either. Hence, the emulator can never be invoked with an
actually illegal RAX.

Outside of forced emulation or code stream rewriting, the emulator
should only be invoked for these instructions in cases such as RAX
having a legal GPA that lies outside guest memory, as the #NPF
interception handler will try to emulate the instruction after failing
to create a proper mapping in the NPT. In this case, the emulator's
responsibility ends with checking pre-intercept exceptions and
intercepts, it does not actually emulate these instructions.

According to the APM, #GP due to invalid op happens after the
interception check:

Generally, instruction intercepts are checked after simple exceptions
(such as #GP—when CPL is incorrect—or #UD) have been checked, but
before exceptions related to memory accesses (such as page faults) and
exceptions based on specific operand values.

Arguably, the emulator's checks for EFER.SVME and intercepts are also
unnecessary. If EFER.SVME is cleared or if L1 intercepts
VMRUN/VMSAVE/VMLOAD (for nested), then KVM should always be intercepting
these instructions anyway, and the emulator should not be invoked (see
above). Leave dealing with that for later.

Fixes: 01de8b09e606 ("KVM: SVM: Add intercept checks for SVM instructions")
Suggested-by: Sean Christopherson <seanjc@xxxxxxxxxx>
Signed-off-by: Yosry Ahmed <yosry@xxxxxxxxxx>
---
arch/x86/kvm/emulate.c | 17 +++--------------
1 file changed, 3 insertions(+), 14 deletions(-)

diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index 6145dac4a605a..a449a00555da1 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -3883,17 +3883,6 @@ static int check_svme(struct x86_emulate_ctxt *ctxt)
return X86EMUL_CONTINUE;
}

-static int check_svme_pa(struct x86_emulate_ctxt *ctxt)
-{
- u64 rax = reg_read(ctxt, VCPU_REGS_RAX);
-
- /* Valid physical address? */
- if (rax & 0xffff000000000000ULL)
- return emulate_gp(ctxt, 0);
-
- return check_svme(ctxt);
-}
-
static int check_rdtsc(struct x86_emulate_ctxt *ctxt)
{
u64 cr4 = ctxt->ops->get_cr(ctxt, 4);
@@ -3997,10 +3986,10 @@ static const struct opcode group7_rm2[] = {
};

static const struct opcode group7_rm3[] = {
- DIP(SrcNone | Prot | Priv, vmrun, check_svme_pa),
+ DIP(SrcNone | Prot | Priv, vmrun, check_svme),
II(SrcNone | Prot | EmulateOnUD, em_hypercall, vmmcall),
- DIP(SrcNone | Prot | Priv, vmload, check_svme_pa),
- DIP(SrcNone | Prot | Priv, vmsave, check_svme_pa),
+ DIP(SrcNone | Prot | Priv, vmload, check_svme),
+ DIP(SrcNone | Prot | Priv, vmsave, check_svme),
DIP(SrcNone | Prot | Priv, stgi, check_svme),
DIP(SrcNone | Prot | Priv, clgi, check_svme),
DIP(SrcNone | Prot | Priv, skinit, check_svme),
--
2.53.0.851.ga537e3e6e9-goog