Re: [PATCH] KVM: VMX: Inject #UD if guest tries to execute SEAMCALL or TDCALL
From: Xiaoyao Li
Date: Wed Oct 15 2025 - 10:45:29 EST
On 10/15/2025 9:57 PM, Sean Christopherson wrote:
On Wed, Oct 15, 2025, Xiaoyao Li wrote:
On 10/15/2025 7:10 AM, Sean Christopherson wrote:
diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
index 76271962cb70..f64a1eb241b6 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -6728,6 +6728,14 @@ static bool nested_vmx_l1_wants_exit(struct kvm_vcpu *vcpu,
case EXIT_REASON_NOTIFY:
/* Notify VM exit is not exposed to L1 */
return false;
+ case EXIT_REASON_SEAMCALL:
+ case EXIT_REASON_TDCALL:
+ /*
+ * SEAMCALL and TDCALL unconditionally VM-Exit, but aren't
+ * virtualized by KVM for L1 hypervisors, i.e. L1 should
+ * never want or expect such an exit.
+ */
The i.e. part is confusing? It is exactly forwarding the EXITs to L1, while
it says L1 should never want or expect such an exit.
Gah, the comment is right, the code is wrong.
So the intent was to return false here? to let L0 handle the exit?
Then I have a question, why not implement it in nested_vmx_l0_wants_exit()? what's the reason and rule here?
/facepalm
I even tried to explicitly test this, but I put the TDCALL and SEAMCALL in L1
instead of L2.
diff --git a/tools/testing/selftests/kvm/x86/vmx_invalid_nested_guest_state.c b/tools/testing/selftests/kvm/x86/vmx_invalid_nested_guest_state.c
index a100ee5f0009..1d7ef7d2d381 100644
--- a/tools/testing/selftests/kvm/x86/vmx_invalid_nested_guest_state.c
+++ b/tools/testing/selftests/kvm/x86/vmx_invalid_nested_guest_state.c
@@ -23,11 +23,17 @@ static void l2_guest_code(void)
: : [port] "d" (ARBITRARY_IO_PORT) : "rax");
}
+#define tdcall ".byte 0x66,0x0f,0x01,0xcc"
+#define seamcall ".byte 0x66,0x0f,0x01,0xcf"
+
static void l1_guest_code(struct vmx_pages *vmx_pages)
{
#define L2_GUEST_STACK_SIZE 64
unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE];
+ TEST_ASSERT_EQ(kvm_asm_safe(tdcall), UD_VECTOR);
+ TEST_ASSERT_EQ(kvm_asm_safe(seamcall), UD_VECTOR);
+
GUEST_ASSERT(prepare_for_vmx_operation(vmx_pages));
GUEST_ASSERT(load_vmcs(vmx_pages));