Files
vfio-native/patches/kvm/0001-KVM-SVM-intercept-GP-when-guest-EFER.SVME-is-clear.patch

95 lines
4.0 KiB
Diff

From 0a6b8d86c62bd4caebf27872019c7f66b753d61b Mon Sep 17 00:00:00 2001
From: Sandwich <sandwich@archworks.co>
Date: Sat, 5 Sep 2026 12:02:50 +0200
Subject: [PATCH 1/5] KVM: SVM: intercept #GP when guest EFER.SVME is clear
svm_set_efer() unconditionally sets EFER_SVME in the VMCB, because
hardware requires it to run the guest at all. A guest that has not
itself enabled SVM therefore still runs with SVME set from the CPU's
point of view.
When such a guest executes an SVM instruction at CPL > 0, hardware
passes the SVME check, observes CPL != 0, and injects #GP before the
instruction intercept can fire. Bare metal, where SVME really is clear,
raises #UD instead. The divergence is directly observable from
unprivileged guest code.
svm_recalc_instruction_intercepts() already carries a FIXME describing
exactly this. Close it by intercepting #GP while the guest's EFER.SVME
is clear, so gp_interception() can decode the instruction and route it
to its real handler, which injects the #UD the architecture specifies.
The intercept is kept across a guest clearing SVME, and dropped again on
SVME=1 where the erratum workaround does not need it.
SEV guests are excluded for the same reason the erratum workaround
excludes them: KVM can't decode their instructions, so the intercept
would only reinject the same #GP. SEV-ES guests additionally clear
the #GP intercept in sev_es_init_vmcb().
For nested guests, gp_interception() already routes a decoded SVM
instruction through nested_svm_check_permissions(), which injects #UD
when L2's SVME is clear. When L1 intercepts #GP itself, the exit is
reflected to L1 as before.
Signed-off-by: Sandwich <sandwich@archworks.co>
---
arch/x86/kvm/svm/svm.c | 22 +++++++++++++++-------
1 file changed, 15 insertions(+), 7 deletions(-)
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index 1158f3e28..b0db02615 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -237,9 +237,11 @@ int svm_set_efer(struct kvm_vcpu *vcpu, u64 efer)
kvm_make_request(KVM_REQ_TRIPLE_FAULT, vcpu);
svm_leave_nested(vcpu);
- /* #GP intercept is still needed for vmware backdoor */
- if (!enable_vmware_backdoor)
- clr_exception_intercept(svm, GP_VECTOR);
+ /*
+ * #GP stays intercepted: with SVME=0 it routes CPL>0
+ * SVM instructions to the #UD the architecture requires,
+ * see svm_recalc_instruction_intercepts().
+ */
/*
* Free the nested guest state, unless we are in SMM.
@@ -263,6 +265,8 @@ int svm_set_efer(struct kvm_vcpu *vcpu, u64 efer)
*/
if (svm_gp_erratum_intercept && !is_sev_guest(vcpu))
set_exception_intercept(svm, GP_VECTOR);
+ else if (!enable_vmware_backdoor)
+ clr_exception_intercept(svm, GP_VECTOR);
}
svm_pmu_handle_nested_transition(svm);
@@ -1073,16 +1077,20 @@ static void svm_recalc_instruction_intercepts(struct kvm_vcpu *vcpu)
* are set when the VMCB is initialized and never cleared (if the
* relevant intercepts are set, the enablements are meaningless anyway).
*
- * FIXME: When #GP is not intercepted, a #GP on these instructions (e.g.
- * due to CPL > 0) could be injected by hardware before the instruction
- * is intercepted, leading to #GP taking precedence over #UD from the
- * guest's perspective.
+ * Because hardware sees SVME=1, a #GP on these instructions (e.g. due
+ * to CPL > 0) is injected before the instruction intercept fires, and
+ * would take precedence over the #UD the guest should observe. Also
+ * intercept #GP so that gp_interception() can decode the instruction
+ * and route it to its handler, which injects the #UD. SEV guests are
+ * excluded as KVM can't decode their instructions.
*/
if (!(vcpu->arch.efer & EFER_SVME)) {
svm_set_intercept(svm, INTERCEPT_VMLOAD);
svm_set_intercept(svm, INTERCEPT_VMSAVE);
svm_set_intercept(svm, INTERCEPT_CLGI);
svm_set_intercept(svm, INTERCEPT_STGI);
+ if (!is_sev_guest(vcpu))
+ set_exception_intercept(svm, GP_VECTOR);
} else {
/*
* If hardware supports Virtual VMLOAD VMSAVE then enable it
--
2.55.0