Kernel KVM virtualization development
 help / color / mirror / Atom feed
* [PATCH v4 0/6] KVM: nVMX: Synthesize SHUTDOWN on RSM with bad state
@ 2026-07-28  0:43 Sean Christopherson
  2026-07-28  0:43 ` [PATCH v4 1/6] KVM: x86: Extract VMX's unhandleable emulation check to common x86 Sean Christopherson
                   ` (5 more replies)
  0 siblings, 6 replies; 10+ messages in thread
From: Sean Christopherson @ 2026-07-28  0:43 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini; +Cc: kvm, linux-kernel, Hao Zhang

Synthesize SHUTDOWN if invalid guest state is detected a RSM, i.e. if SMRAM
is clobbered by L1 (or host userspace) while handling an SMI that occurred
while L2 was active.  This fixes yet another case of syzkaller tripping KVM's
sanity check that KVM doesn't cancel a pending nested VM-Enter.

Hao, can you provide your Signed-off-by for the selftest, assuming it looks
good to you?  Thanks!

v4:
 - Synthesize SHUTDOWN instead of trying to suppress the KVM_BUG_ON().
 - Add a selftest.

v3:
 - https://lore.kernel.org/all/al8M4gCwsWXS_jMs@192.168.1.215
 - Retain KVM_NESTED_RUN_PENDING_UNTRUSTED until after
   sync_vmcs02_to_vmcs12(), to avoid saving VMCS12 fields that are valid
   only after L2 has actually run.
 - Clear the untrusted pending state before restoring L1 state to avoid
   leaking nested_run_pending into L1 and blocking event injection.
 - Clarify the VMX pending-run BUG comment and update the changelog.
 - Tested with the syzkaller repro on the fixed kernel, no WARNING/KVM_BUG
   in the repro log

v2:
 - https://lore.kernel.org/all/al8M4gCwsWXS_jMs@192.168.1.215
 - Mark nested state restored by RSM from SMRAM as KVM_NESTED_RUN_PENDING_UNTRUSTED.
 - Keep the BUG check in __vmx_handle_exit(), but make it apply only to KVM_NESTED_RUN_PENDING.

v1: https://lore.kernel.org/all/al3Qbq-jUYE-_72N@192.168.1.215

Hao Zhang (1):
  KVM: selftests: Extend the invalid nVMX guest state test to cover RSM

Sean Christopherson (5):
  KVM: x86: Extract VMX's unhandleable emulation check to common x86
  KVM: nVMX: Synthesize SHUTDOWN on RSM if L2 requires emulation
  KVM: x86: Rework kvm_x86_ops.vcpu_pre_run() into
    .vcpu_needs_initialization()
  KVM: selftests: Use port 0x80 in invalid nVMX guest state test
  KVM: selftests: Refactor invalid nVMX state test to prepare for RSM
    testcase

 arch/x86/include/asm/kvm-x86-ops.h            |   3 +-
 arch/x86/include/asm/kvm_host.h               |   4 +-
 arch/x86/kvm/smm.c                            |   4 +
 arch/x86/kvm/svm/sev.c                        |   5 +
 arch/x86/kvm/svm/svm.c                        |  12 +-
 arch/x86/kvm/svm/svm.h                        |   1 +
 arch/x86/kvm/vmx/main.c                       |  21 +++-
 arch/x86/kvm/vmx/tdx.c                        |   9 +-
 arch/x86/kvm/vmx/vmx.c                        |  12 +-
 arch/x86/kvm/vmx/x86_ops.h                    |   4 +-
 arch/x86/kvm/x86.c                            |  10 +-
 .../kvm/x86/vmx_invalid_nested_guest_state.c  | 118 ++++++++++++++----
 12 files changed, 143 insertions(+), 60 deletions(-)


base-commit: 271255273d5ff348fe29d89fe4712b2f7f7907c3
-- 
2.55.0.229.g6434b31f56-goog


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v4 1/6] KVM: x86: Extract VMX's unhandleable emulation check to common x86
  2026-07-28  0:43 [PATCH v4 0/6] KVM: nVMX: Synthesize SHUTDOWN on RSM with bad state Sean Christopherson
@ 2026-07-28  0:43 ` Sean Christopherson
  2026-07-28  0:58   ` sashiko-bot
  2026-07-28  0:43 ` [PATCH v4 2/6] KVM: nVMX: Synthesize SHUTDOWN on RSM if L2 requires emulation Sean Christopherson
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 10+ messages in thread
From: Sean Christopherson @ 2026-07-28  0:43 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini; +Cc: kvm, linux-kernel, Hao Zhang

Expose VMX's check for unhandleable emulation as its own kvm_x86_ops hook,
and move the actual pre-KVM_RUN check into common x86.  This will allow
sharing the core logic with KVM's RSM emulation without needed to add a
post-RSM hook, and is a step towards removing the .vcpu_pre_run() hook
entirely.

Alternatively, KVM could provide a post-RSM hook as mentioned, but pre/post
hooks tend to be unwieldy as the exact "timing" of the call often matters
greatly.  E.g. in this case, the call must slot in exactly between loading
guest state from SMRAM and the hack to force the vCPU out of L2 on SHUTDOWN.

Signed-off-by: Sean Christopherson <seanjc@google.com>
---
 arch/x86/include/asm/kvm-x86-ops.h |  1 +
 arch/x86/include/asm/kvm_host.h    |  2 ++
 arch/x86/kvm/vmx/main.c            | 15 +++++++++++++--
 arch/x86/kvm/vmx/vmx.c             | 12 +-----------
 arch/x86/kvm/vmx/x86_ops.h         |  2 +-
 arch/x86/kvm/x86.c                 |  5 +++++
 6 files changed, 23 insertions(+), 14 deletions(-)

diff --git a/arch/x86/include/asm/kvm-x86-ops.h b/arch/x86/include/asm/kvm-x86-ops.h
index 5cb132eca3c3..e5b0458afc25 100644
--- a/arch/x86/include/asm/kvm-x86-ops.h
+++ b/arch/x86/include/asm/kvm-x86-ops.h
@@ -68,6 +68,7 @@ KVM_X86_OP(vcpu_run)
 KVM_X86_OP(handle_exit)
 KVM_X86_OP(skip_emulated_instruction)
 KVM_X86_OP_OPTIONAL(update_emulated_instruction)
+KVM_X86_OP_OPTIONAL_RET0(unhandleable_emulation_required)
 KVM_X86_OP(set_interrupt_shadow)
 KVM_X86_OP(get_interrupt_shadow)
 KVM_X86_OP(patch_hypercall)
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 7a258831616f..7e3cacb2df9b 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -1593,6 +1593,8 @@ struct kvm_x86_ops {
 		enum exit_fastpath_completion exit_fastpath);
 	int (*skip_emulated_instruction)(struct kvm_vcpu *vcpu);
 	void (*update_emulated_instruction)(struct kvm_vcpu *vcpu);
+	bool (*unhandleable_emulation_required)(struct kvm_vcpu *vcpu);
+
 	void (*set_interrupt_shadow)(struct kvm_vcpu *vcpu, int mask);
 	u32 (*get_interrupt_shadow)(struct kvm_vcpu *vcpu);
 	void (*patch_hypercall)(struct kvm_vcpu *vcpu,
diff --git a/arch/x86/kvm/vmx/main.c b/arch/x86/kvm/vmx/main.c
index 04f986e3d439..924a629c21e2 100644
--- a/arch/x86/kvm/vmx/main.c
+++ b/arch/x86/kvm/vmx/main.c
@@ -145,7 +145,7 @@ static int vt_vcpu_pre_run(struct kvm_vcpu *vcpu)
 	if (is_td_vcpu(vcpu))
 		return tdx_vcpu_pre_run(vcpu);
 
-	return vmx_vcpu_pre_run(vcpu);
+	return 1;
 }
 
 static fastpath_t vt_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags)
@@ -165,6 +165,16 @@ static int vt_handle_exit(struct kvm_vcpu *vcpu,
 	return vmx_handle_exit(vcpu, fastpath);
 }
 
+static bool vt_unhandleable_emulation_required(struct kvm_vcpu *vcpu)
+{
+	if (is_td_vcpu(vcpu)) {
+		WARN_ON_ONCE(to_vt(vcpu)->emulation_required);
+		return false;
+	}
+
+	return vmx_unhandleable_emulation_required(vcpu);
+}
+
 static int vt_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
 {
 	if (unlikely(is_td_vcpu(vcpu)))
@@ -939,11 +949,12 @@ struct kvm_x86_ops vt_x86_ops __initdata = {
 	.flush_tlb_gva = vt_op(flush_tlb_gva),
 	.flush_tlb_guest = vt_op(flush_tlb_guest),
 
-	.vcpu_pre_run = vt_op(vcpu_pre_run),
+	.vcpu_pre_run = vt_op_tdx_only(vcpu_pre_run),
 	.vcpu_run = vt_op(vcpu_run),
 	.handle_exit = vt_op(handle_exit),
 	.skip_emulated_instruction = vmx_skip_emulated_instruction,
 	.update_emulated_instruction = vmx_update_emulated_instruction,
+	.unhandleable_emulation_required = vt_op(unhandleable_emulation_required),
 	.set_interrupt_shadow = vt_op(set_interrupt_shadow),
 	.get_interrupt_shadow = vt_op(get_interrupt_shadow),
 	.patch_hypercall = vt_op(patch_hypercall),
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index e4b9ac7fed9f..76160adf8297 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -6035,7 +6035,7 @@ static int handle_nmi_window(struct kvm_vcpu *vcpu)
  * with unsrestricted guest mode disabled) and KVM can't faithfully emulate the
  * current vCPU state.
  */
-static bool vmx_unhandleable_emulation_required(struct kvm_vcpu *vcpu)
+bool vmx_unhandleable_emulation_required(struct kvm_vcpu *vcpu)
 {
 	struct vcpu_vmx *vmx = to_vmx(vcpu);
 
@@ -6110,16 +6110,6 @@ static int handle_invalid_guest_state(struct kvm_vcpu *vcpu)
 	return 1;
 }
 
-int vmx_vcpu_pre_run(struct kvm_vcpu *vcpu)
-{
-	if (vmx_unhandleable_emulation_required(vcpu)) {
-		kvm_prepare_emulation_failure_exit(vcpu);
-		return 0;
-	}
-
-	return 1;
-}
-
 /*
  * Indicate a busy-waiting vcpu in spinlock. We do not enable the PAUSE
  * exiting, so only get here on cpu with PAUSE-Loop-Exiting.
diff --git a/arch/x86/kvm/vmx/x86_ops.h b/arch/x86/kvm/vmx/x86_ops.h
index 409858074246..4d2dd88afe7f 100644
--- a/arch/x86/kvm/vmx/x86_ops.h
+++ b/arch/x86/kvm/vmx/x86_ops.h
@@ -21,7 +21,6 @@ int vmx_vm_init(struct kvm *kvm);
 void vmx_vm_destroy(struct kvm *kvm);
 int vmx_vcpu_precreate(struct kvm *kvm);
 int vmx_vcpu_create(struct kvm_vcpu *vcpu);
-int vmx_vcpu_pre_run(struct kvm_vcpu *vcpu);
 fastpath_t vmx_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags);
 void vmx_vcpu_free(struct kvm_vcpu *vcpu);
 void vmx_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event);
@@ -31,6 +30,7 @@ int vmx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t exit_fastpath);
 void vmx_handle_exit_irqoff(struct kvm_vcpu *vcpu);
 int vmx_skip_emulated_instruction(struct kvm_vcpu *vcpu);
 void vmx_update_emulated_instruction(struct kvm_vcpu *vcpu);
+bool vmx_unhandleable_emulation_required(struct kvm_vcpu *vcpu);
 int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info);
 #ifdef CONFIG_KVM_SMM
 int vmx_smi_allowed(struct kvm_vcpu *vcpu, bool for_injection);
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 0f1a829032c0..64a13acc37be 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -8867,6 +8867,11 @@ static int kvm_x86_vcpu_pre_run(struct kvm_vcpu *vcpu)
 	    !kvm_apic_init_sipi_allowed(vcpu))
 		return -EINVAL;
 
+	if (kvm_x86_call(unhandleable_emulation_required)(vcpu)) {
+		kvm_prepare_emulation_failure_exit(vcpu);
+		return 0;
+	}
+
 	return kvm_x86_call(vcpu_pre_run)(vcpu);
 }
 
-- 
2.55.0.229.g6434b31f56-goog


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v4 2/6] KVM: nVMX: Synthesize SHUTDOWN on RSM if L2 requires emulation
  2026-07-28  0:43 [PATCH v4 0/6] KVM: nVMX: Synthesize SHUTDOWN on RSM with bad state Sean Christopherson
  2026-07-28  0:43 ` [PATCH v4 1/6] KVM: x86: Extract VMX's unhandleable emulation check to common x86 Sean Christopherson
@ 2026-07-28  0:43 ` Sean Christopherson
  2026-07-28  0:43 ` [PATCH v4 3/6] KVM: x86: Rework kvm_x86_ops.vcpu_pre_run() into .vcpu_needs_initialization() Sean Christopherson
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 10+ messages in thread
From: Sean Christopherson @ 2026-07-28  0:43 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini; +Cc: kvm, linux-kernel, Hao Zhang

Synthesize SHUTDOWN (for L1) if L2 requires unhandleable emulation after
loading guest state from SMRAM during RSM to prevent a misbehaving L1 (or
userspace via L1) from tripping the sanity check that KVM doesn't try to
cancel a pending nested VM-Enter.  If SMRAM is modified such that RSM will
load what should be impossible state for L2, then KVM will detect that it
needs to emulate the current code stream and will abort VM-Entry to L2.
And because KVM (rightly) expects such a scenario to be impossible, KVM
WARNs and bugs the VM.

  __ret && !(vcpu->kvm)->vm_bugged
  WARNING: arch/x86/kvm/vmx/vmx.c:6741 at vmx_handle_exit+0x65/0x790 [kvm_intel], CPU#13: vmx_invalid_nes/2902
  Modules linked in: kvm_intel kvm irqbypass [last unloaded: kvm]
  CPU: 13 UID: 1000 PID: 2902 Comm: vmx_invalid_nes Tainted: G        W 7.2.0-rc2 #124 PREEMPT
  Tainted: [W]=WARN
  Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 0.0.0 02/06/2015
  RIP: 0010:vmx_handle_exit+0x65/0x790 [kvm_intel]
  Call Trace:
   <TASK>
   kvm_arch_vcpu_ioctl_run+0xdf8/0x1d00 [kvm]
   kvm_vcpu_ioctl+0x2d5/0x960 [kvm]
   __x64_sys_ioctl+0x8a/0xd0
   do_syscall_64+0xb7/0x570
   entry_SYSCALL_64_after_hwframe+0x4b/0x53
   </TASK>

Alternatively, KVM could suppress the WARN for the RSM case, but that would
still leave the vCPU in a "bad" state that KVM doesn't know how to handle
(which is also why KVM rejects attempts to do KVM_RUN when the vCPU is
loaded with invalid state).  And architecturally, the Intel SDM explicitly
states that RSM leads to shutdown if the CPU detects invalid state.

Fixes: 2bb8cafea80b ("KVM: vVMX: signal failure for nested VMEntry if emulation_required")
Reported-by: Hao Zhang <zhanghao1@kylinos.cn>
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
 arch/x86/kvm/smm.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/arch/x86/kvm/smm.c b/arch/x86/kvm/smm.c
index a446487bdd5c..656a38dad7e7 100644
--- a/arch/x86/kvm/smm.c
+++ b/arch/x86/kvm/smm.c
@@ -649,6 +649,10 @@ int emulator_leave_smm(struct x86_emulate_ctxt *ctxt)
 #endif
 		ret = rsm_load_state_32(ctxt, &smram.smram32);
 
+	if (ret == X86EMUL_CONTINUE &&
+	    kvm_x86_call(unhandleable_emulation_required)(vcpu))
+		ret = X86EMUL_UNHANDLEABLE;
+
 	/*
 	 * If RSM fails and triggers shutdown, architecturally the shutdown
 	 * occurs *before* the transition to guest mode.  But due to KVM's
-- 
2.55.0.229.g6434b31f56-goog


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v4 3/6] KVM: x86: Rework kvm_x86_ops.vcpu_pre_run() into .vcpu_needs_initialization()
  2026-07-28  0:43 [PATCH v4 0/6] KVM: nVMX: Synthesize SHUTDOWN on RSM with bad state Sean Christopherson
  2026-07-28  0:43 ` [PATCH v4 1/6] KVM: x86: Extract VMX's unhandleable emulation check to common x86 Sean Christopherson
  2026-07-28  0:43 ` [PATCH v4 2/6] KVM: nVMX: Synthesize SHUTDOWN on RSM if L2 requires emulation Sean Christopherson
@ 2026-07-28  0:43 ` Sean Christopherson
  2026-07-28  0:43 ` [PATCH v4 4/6] KVM: selftests: Use port 0x80 in invalid nVMX guest state test Sean Christopherson
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 10+ messages in thread
From: Sean Christopherson @ 2026-07-28  0:43 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini; +Cc: kvm, linux-kernel, Hao Zhang

Rework .vcpu_pre_run() into a more specific .vcpu_needs_initialization() to
consolidate the SNP and TDX control flows, and to eliminate the potentially
confusing almost-collision between svm_vcpu_pre_run() and pre_sev_run(): the
former is SEV specific, but is pre-KVM_RUN, whereas the latter is pre-VMRUN.

Signed-off-by: Sean Christopherson <seanjc@google.com>
---
 arch/x86/include/asm/kvm-x86-ops.h |  2 +-
 arch/x86/include/asm/kvm_host.h    |  2 +-
 arch/x86/kvm/svm/sev.c             |  5 +++++
 arch/x86/kvm/svm/svm.c             | 12 +-----------
 arch/x86/kvm/svm/svm.h             |  1 +
 arch/x86/kvm/vmx/main.c            | 10 ++++------
 arch/x86/kvm/vmx/tdx.c             |  9 +++------
 arch/x86/kvm/vmx/x86_ops.h         |  2 +-
 arch/x86/kvm/x86.c                 |  5 ++++-
 9 files changed, 21 insertions(+), 27 deletions(-)

diff --git a/arch/x86/include/asm/kvm-x86-ops.h b/arch/x86/include/asm/kvm-x86-ops.h
index e5b0458afc25..e213c9ae3e30 100644
--- a/arch/x86/include/asm/kvm-x86-ops.h
+++ b/arch/x86/include/asm/kvm-x86-ops.h
@@ -63,7 +63,7 @@ KVM_X86_OP_OPTIONAL(flush_remote_tlbs_range)
 #endif
 KVM_X86_OP(flush_tlb_gva)
 KVM_X86_OP(flush_tlb_guest)
-KVM_X86_OP(vcpu_pre_run)
+KVM_X86_OP_OPTIONAL_RET0(vcpu_needs_initialization)
 KVM_X86_OP(vcpu_run)
 KVM_X86_OP(handle_exit)
 KVM_X86_OP(skip_emulated_instruction)
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 7e3cacb2df9b..283847619ff8 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -1586,7 +1586,7 @@ struct kvm_x86_ops {
 	 */
 	void (*flush_tlb_guest)(struct kvm_vcpu *vcpu);
 
-	int (*vcpu_pre_run)(struct kvm_vcpu *vcpu);
+	bool (*vcpu_needs_initialization)(struct kvm_vcpu *vcpu);
 	enum exit_fastpath_completion (*vcpu_run)(struct kvm_vcpu *vcpu,
 						  u64 run_flags);
 	int (*handle_exit)(struct kvm_vcpu *vcpu,
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index 90a08d36d843..fcb41dfde4c0 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -3556,6 +3556,11 @@ void sev_free_vcpu(struct kvm_vcpu *vcpu)
 	__sev_es_unmap_ghcb(svm);
 }
 
+bool sev_vcpu_needs_initialization(struct kvm_vcpu *vcpu)
+{
+	return to_kvm_sev_info(vcpu->kvm)->need_init;
+}
+
 int pre_sev_run(struct vcpu_svm *svm, int cpu)
 {
 	struct svm_cpu_data *sd = per_cpu_ptr(&svm_data, cpu);
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index d68cba12c772..bccca9c5b966 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -4402,16 +4402,6 @@ static void svm_cancel_injection(struct kvm_vcpu *vcpu)
 	svm_complete_interrupts(vcpu);
 }
 
-static int svm_vcpu_pre_run(struct kvm_vcpu *vcpu)
-{
-#ifdef CONFIG_KVM_AMD_SEV
-	if (to_kvm_sev_info(vcpu->kvm)->need_init)
-		return -EINVAL;
-#endif
-
-	return 1;
-}
-
 static fastpath_t svm_exit_handlers_fastpath(struct kvm_vcpu *vcpu)
 {
 	struct vcpu_svm *svm = to_svm(vcpu);
@@ -5388,7 +5378,6 @@ struct kvm_x86_ops svm_x86_ops __initdata = {
 	.flush_tlb_gva = svm_flush_tlb_gva,
 	.flush_tlb_guest = svm_flush_tlb_guest,
 
-	.vcpu_pre_run = svm_vcpu_pre_run,
 	.vcpu_run = svm_vcpu_run,
 	.handle_exit = svm_handle_exit,
 	.skip_emulated_instruction = svm_skip_emulated_instruction,
@@ -5446,6 +5435,7 @@ struct kvm_x86_ops svm_x86_ops __initdata = {
 #endif
 
 #ifdef CONFIG_KVM_AMD_SEV
+	.vcpu_needs_initialization = sev_vcpu_needs_initialization,
 	.dev_get_attr = sev_dev_get_attr,
 	.mem_enc_ioctl = sev_mem_enc_ioctl,
 	.mem_enc_register_region = sev_mem_enc_register_region,
diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
index a327bf751ecd..66b44b54608e 100644
--- a/arch/x86/kvm/svm/svm.h
+++ b/arch/x86/kvm/svm/svm.h
@@ -979,6 +979,7 @@ void sev_es_prepare_switch_to_guest(struct vcpu_svm *svm, struct sev_es_save_are
 void sev_es_unmap_ghcb(struct vcpu_svm *svm);
 
 #ifdef CONFIG_KVM_AMD_SEV
+bool sev_vcpu_needs_initialization(struct kvm_vcpu *vcpu);
 int sev_mem_enc_ioctl(struct kvm *kvm, void __user *argp);
 int sev_mem_enc_register_region(struct kvm *kvm,
 				struct kvm_enc_region *range);
diff --git a/arch/x86/kvm/vmx/main.c b/arch/x86/kvm/vmx/main.c
index 924a629c21e2..0ff3230fd95e 100644
--- a/arch/x86/kvm/vmx/main.c
+++ b/arch/x86/kvm/vmx/main.c
@@ -140,12 +140,10 @@ static void vt_vcpu_put(struct kvm_vcpu *vcpu)
 	vmx_vcpu_put(vcpu);
 }
 
-static int vt_vcpu_pre_run(struct kvm_vcpu *vcpu)
+static bool vt_vcpu_needs_initialization(struct kvm_vcpu *vcpu)
 {
-	if (is_td_vcpu(vcpu))
-		return tdx_vcpu_pre_run(vcpu);
-
-	return 1;
+	return is_td_vcpu(vcpu) &&
+	       tdx_vcpu_needs_initialization(vcpu);
 }
 
 static fastpath_t vt_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags)
@@ -949,7 +947,7 @@ struct kvm_x86_ops vt_x86_ops __initdata = {
 	.flush_tlb_gva = vt_op(flush_tlb_gva),
 	.flush_tlb_guest = vt_op(flush_tlb_guest),
 
-	.vcpu_pre_run = vt_op_tdx_only(vcpu_pre_run),
+	.vcpu_needs_initialization = vt_op_tdx_only(vcpu_needs_initialization),
 	.vcpu_run = vt_op(vcpu_run),
 	.handle_exit = vt_op(handle_exit),
 	.skip_emulated_instruction = vmx_skip_emulated_instruction,
diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c
index d1af0a752e97..b272c20586a7 100644
--- a/arch/x86/kvm/vmx/tdx.c
+++ b/arch/x86/kvm/vmx/tdx.c
@@ -897,13 +897,10 @@ void tdx_vcpu_free(struct kvm_vcpu *vcpu)
 	tdx->state = VCPU_TD_STATE_UNINITIALIZED;
 }
 
-int tdx_vcpu_pre_run(struct kvm_vcpu *vcpu)
+bool tdx_vcpu_needs_initialization(struct kvm_vcpu *vcpu)
 {
-	if (unlikely(to_tdx(vcpu)->state != VCPU_TD_STATE_INITIALIZED ||
-		     to_kvm_tdx(vcpu->kvm)->state != TD_STATE_RUNNABLE))
-		return -EINVAL;
-
-	return 1;
+	return to_tdx(vcpu)->state != VCPU_TD_STATE_INITIALIZED ||
+	       to_kvm_tdx(vcpu->kvm)->state != TD_STATE_RUNNABLE;
 }
 
 static __always_inline u32 tdcall_to_vmx_exit_reason(struct kvm_vcpu *vcpu)
diff --git a/arch/x86/kvm/vmx/x86_ops.h b/arch/x86/kvm/vmx/x86_ops.h
index 4d2dd88afe7f..cdb38d940cfb 100644
--- a/arch/x86/kvm/vmx/x86_ops.h
+++ b/arch/x86/kvm/vmx/x86_ops.h
@@ -137,7 +137,7 @@ int tdx_vcpu_create(struct kvm_vcpu *vcpu);
 void tdx_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event);
 void tdx_vcpu_free(struct kvm_vcpu *vcpu);
 void tdx_vcpu_load(struct kvm_vcpu *vcpu, int cpu);
-int tdx_vcpu_pre_run(struct kvm_vcpu *vcpu);
+bool tdx_vcpu_needs_initialization(struct kvm_vcpu *vcpu);
 fastpath_t tdx_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags);
 void tdx_prepare_switch_to_guest(struct kvm_vcpu *vcpu);
 void tdx_vcpu_put(struct kvm_vcpu *vcpu);
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 64a13acc37be..d94b59140c45 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -8867,12 +8867,15 @@ static int kvm_x86_vcpu_pre_run(struct kvm_vcpu *vcpu)
 	    !kvm_apic_init_sipi_allowed(vcpu))
 		return -EINVAL;
 
+	if (kvm_x86_call(vcpu_needs_initialization)(vcpu))
+		return -EINVAL;
+
 	if (kvm_x86_call(unhandleable_emulation_required)(vcpu)) {
 		kvm_prepare_emulation_failure_exit(vcpu);
 		return 0;
 	}
 
-	return kvm_x86_call(vcpu_pre_run)(vcpu);
+	return 1;
 }
 
 int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
-- 
2.55.0.229.g6434b31f56-goog


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v4 4/6] KVM: selftests: Use port 0x80 in invalid nVMX guest state test
  2026-07-28  0:43 [PATCH v4 0/6] KVM: nVMX: Synthesize SHUTDOWN on RSM with bad state Sean Christopherson
                   ` (2 preceding siblings ...)
  2026-07-28  0:43 ` [PATCH v4 3/6] KVM: x86: Rework kvm_x86_ops.vcpu_pre_run() into .vcpu_needs_initialization() Sean Christopherson
@ 2026-07-28  0:43 ` Sean Christopherson
  2026-07-28  0:43 ` [PATCH v4 5/6] KVM: selftests: Refactor invalid nVMX state test to prepare for RSM testcase Sean Christopherson
  2026-07-28  0:43 ` [PATCH v4 6/6] KVM: selftests: Extend the invalid nVMX guest state test to cover RSM Sean Christopherson
  5 siblings, 0 replies; 10+ messages in thread
From: Sean Christopherson @ 2026-07-28  0:43 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini; +Cc: kvm, linux-kernel, Hao Zhang

Use port 0x80 instead of 0x2000 to trigger an L2 => L0 VM-Exit in the nVMX
invalid guest state test, so that the test can be expanded to cover RSM in
the L2 => SMI => L2 path without having to manually encode a MOV to DX (the
immediate form of IN only supports an imm8).

Use port 0x80 as it's a well-known port and is used by other tests, e.g. in
the coalesced I/O test.

Signed-off-by: Sean Christopherson <seanjc@google.com>
---
 .../selftests/kvm/x86/vmx_invalid_nested_guest_state.c      | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

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 578283893ab3..fb9444ca0d7e 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
@@ -9,7 +9,7 @@
 
 #include "kselftest.h"
 
-#define ARBITRARY_IO_PORT 0x2000
+#define ARBITRARY_IO_PORT 0x80
 
 static struct kvm_vm *vm;
 
@@ -19,8 +19,8 @@ static void l2_guest_code(void)
 	 * Generate an exit to L0 userspace, i.e. main(), via I/O to an
 	 * arbitrary port.
 	 */
-	asm volatile("inb %%dx, %%al"
-		     : : [port] "d" (ARBITRARY_IO_PORT) : "rax");
+	asm volatile("inb $" __stringify(ARBITRARY_IO_PORT) ", %%al"
+		     ::: "rax");
 }
 
 static void l1_guest_code(struct vmx_pages *vmx_pages)
-- 
2.55.0.229.g6434b31f56-goog


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v4 5/6] KVM: selftests: Refactor invalid nVMX state test to prepare for RSM testcase
  2026-07-28  0:43 [PATCH v4 0/6] KVM: nVMX: Synthesize SHUTDOWN on RSM with bad state Sean Christopherson
                   ` (3 preceding siblings ...)
  2026-07-28  0:43 ` [PATCH v4 4/6] KVM: selftests: Use port 0x80 in invalid nVMX guest state test Sean Christopherson
@ 2026-07-28  0:43 ` Sean Christopherson
  2026-07-28  0:51   ` sashiko-bot
  2026-07-28  0:43 ` [PATCH v4 6/6] KVM: selftests: Extend the invalid nVMX guest state test to cover RSM Sean Christopherson
  5 siblings, 1 reply; 10+ messages in thread
From: Sean Christopherson @ 2026-07-28  0:43 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini; +Cc: kvm, linux-kernel, Hao Zhang

In the invalid nVMX guest state test, extract the creation of the VM and
initial running of the vCPU to get to L2 into helpers so that the common
code can be reused to extend the test to also cover RSM.

Eliminate the unnecessary global "vm", and opportunistically free the VM
after the testcase as there's zero reason not to.

Opportunistically assert that L2 is never resumed after the I/O exit to L1,
e.g. to guard against false passes.

Signed-off-by: Sean Christopherson <seanjc@google.com>
---
 .../kvm/x86/vmx_invalid_nested_guest_state.c  | 61 +++++++++++++------
 1 file changed, 42 insertions(+), 19 deletions(-)

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 fb9444ca0d7e..ab00265d6c94 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
@@ -11,8 +11,6 @@
 
 #define ARBITRARY_IO_PORT 0x80
 
-static struct kvm_vm *vm;
-
 static void l2_guest_code(void)
 {
 	/*
@@ -21,6 +19,7 @@ static void l2_guest_code(void)
 	 */
 	asm volatile("inb $" __stringify(ARBITRARY_IO_PORT) ", %%al"
 		     ::: "rax");
+	GUEST_FAIL("L2 resumed after stuffing invalid guest state");
 }
 
 static void l1_guest_code(struct vmx_pages *vmx_pages)
@@ -46,35 +45,50 @@ static void l1_guest_code(struct vmx_pages *vmx_pages)
 	GUEST_DONE();
 }
 
-int main(int argc, char *argv[])
+static void vcpu_run_to_io(struct kvm_vcpu *vcpu, bool want_l2)
+{
+	struct kvm_run *run = vcpu->run;
+
+	vcpu_run(vcpu);
+
+	TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
+
+	TEST_ASSERT(run->io.port == ARBITRARY_IO_PORT &&
+		    (!!(run->flags & KVM_RUN_X86_GUEST_MODE) == want_l2  ||
+		     !kvm_has_cap(KVM_CAP_X86_GUEST_MODE)),
+		    "Expected IN from port %d from L2, got port %d from L%u",
+		    ARBITRARY_IO_PORT, run->io.port,
+		    1 + !!(run->flags & KVM_RUN_X86_GUEST_MODE));
+}
+
+static struct kvm_vm *vm_create_and_run_l2(struct kvm_vcpu **vcpu)
 {
 	gva_t vmx_pages_gva;
-	struct kvm_sregs sregs;
-	struct kvm_vcpu *vcpu;
-	struct kvm_run *run;
-	struct ucall uc;
+	struct kvm_vm *vm;
 
-	TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX));
-
-	vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code);
+	vm = vm_create_with_one_vcpu(vcpu, l1_guest_code);
 
 	/* Allocate VMX pages and shared descriptors (vmx_pages). */
 	vcpu_alloc_vmx(vm, &vmx_pages_gva);
-	vcpu_args_set(vcpu, 1, vmx_pages_gva);
-
-	vcpu_run(vcpu);
-
-	run = vcpu->run;
+	vcpu_args_set(*vcpu, 1, vmx_pages_gva);
 
 	/*
 	 * The first exit to L0 userspace should be an I/O access from L2.
 	 * Running L1 should launch L2 without triggering an exit to userspace.
 	 */
-	TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
+	vcpu_run_to_io(*vcpu, true);
 
-	TEST_ASSERT(run->io.port == ARBITRARY_IO_PORT,
-		    "Expected IN from port %d from L2, got port %d",
-		    ARBITRARY_IO_PORT, run->io.port);
+	return vm;
+}
+
+static void test_invalid_l2_guest_state(void)
+{
+	struct kvm_sregs sregs;
+	struct kvm_vcpu *vcpu;
+	struct kvm_vm *vm;
+	struct ucall uc;
+
+	vm = vm_create_and_run_l2(&vcpu);
 
 	/*
 	 * Stuff invalid guest state for L2 by making TR unusable.  The next
@@ -96,4 +110,13 @@ int main(int argc, char *argv[])
 	default:
 		TEST_FAIL("Unexpected ucall: %lu", uc.cmd);
 	}
+
+	kvm_vm_free(vm);
+}
+
+int main(int argc, char *argv[])
+{
+	TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX));
+
+	test_invalid_l2_guest_state();
 }
-- 
2.55.0.229.g6434b31f56-goog


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v4 6/6] KVM: selftests: Extend the invalid nVMX guest state test to cover RSM
  2026-07-28  0:43 [PATCH v4 0/6] KVM: nVMX: Synthesize SHUTDOWN on RSM with bad state Sean Christopherson
                   ` (4 preceding siblings ...)
  2026-07-28  0:43 ` [PATCH v4 5/6] KVM: selftests: Refactor invalid nVMX state test to prepare for RSM testcase Sean Christopherson
@ 2026-07-28  0:43 ` Sean Christopherson
  5 siblings, 0 replies; 10+ messages in thread
From: Sean Christopherson @ 2026-07-28  0:43 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini; +Cc: kvm, linux-kernel, Hao Zhang

From: Hao Zhang <hao_zhang_kdev@163.com>

Extend the invalid nVMX guest state to cover RSM, i.e. to validate that KVM
synthesizes SHUTDOWN for L1 if SMRAM is clobbered with invalid guest state
during an L2 => SMI => RSM => L2 sequence.

Note, unlike the existing testcase, clobbering SMRAM should result in L1,
not L2, getting SHUTDOWN / TRIPLE_FAULT, as RSM is architecturally defined
to trigger shutdown if the CPU detects invalid state.

Not-yet-signed-off-by: Hao Zhang <hao_zhang_kdev@163.com>
Co-developed-by: Sean Christopherson <seanjc@google.com>
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
 .../kvm/x86/vmx_invalid_nested_guest_state.c  | 53 +++++++++++++++++++
 1 file changed, 53 insertions(+)

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 ab00265d6c94..6f546deb193a 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
@@ -2,6 +2,7 @@
 #include "test_util.h"
 #include "kvm_util.h"
 #include "processor.h"
+#include "smm.h"
 #include "vmx.h"
 
 #include <string.h>
@@ -11,6 +12,22 @@
 
 #define ARBITRARY_IO_PORT 0x80
 
+/*
+ * The 64-bit SMRAM state-save area starts at SMBASE + 0xfe00.  TR starts at
+ * offset 0xfe90, and attributes is the second 16-bit field in the descriptor.
+ */
+#define SMRAM64_TR_ATTRIBUTES_OFFSET	0xfe92
+#define SMRAM_GPA			0x1000000
+
+/*
+ * SMI handler that runs in 16-bit Real Mode.  Syncs with L0 via port I/O, then
+ * executes RSM to trigger the consumption of invalid guest state.
+ */
+static u8 smi_handler[] = {
+	0xe4, ARBITRARY_IO_PORT,	/* IN $ARBITRARY_IO_PORT, %al */
+	0x0f, 0xaa,			/* RSM */
+};
+
 static void l2_guest_code(void)
 {
 	/*
@@ -114,9 +131,45 @@ static void test_invalid_l2_guest_state(void)
 	kvm_vm_free(vm);
 }
 
+static void test_invalid_l2_guest_state_rsm(void)
+{
+	struct kvm_vcpu *vcpu;
+	struct kvm_vm *vm;
+	u16 *tr_attrs;
+
+	if (!kvm_has_cap(KVM_CAP_X86_SMM))
+		return;
+
+	vm = vm_create_and_run_l2(&vcpu);
+
+	/*
+	 * Inject SMI while L2 is active, run the vCPU to get I/O exit from L1,
+	 * then stuff TR in the SMRAM state-save area so that RSM restores
+	 * invalid L2 state.
+	 */
+	setup_smram(vm, vcpu, SMRAM_GPA, smi_handler, sizeof(smi_handler));
+	inject_smi(vcpu);
+
+	vcpu_run_to_io(vcpu, false);
+
+	/* Clear the present bit in SMRAM to make TR unusable. */
+	tr_attrs = addr_gpa2hva(vm, SMRAM_GPA + SMRAM64_TR_ATTRIBUTES_OFFSET);
+	*tr_attrs &= ~BIT(7);
+
+	vcpu_run(vcpu);
+
+	/*
+	 * For RSM, L1 gets the SHUTDOWN because RSM is architecturally defined
+	 * to result in shutdown if the CPU detects invalid state in SMRAM.
+	 */
+	TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_SHUTDOWN);
+	kvm_vm_free(vm);
+}
+
 int main(int argc, char *argv[])
 {
 	TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX));
 
 	test_invalid_l2_guest_state();
+	test_invalid_l2_guest_state_rsm();
 }
-- 
2.55.0.229.g6434b31f56-goog


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH v4 5/6] KVM: selftests: Refactor invalid nVMX state test to prepare for RSM testcase
  2026-07-28  0:43 ` [PATCH v4 5/6] KVM: selftests: Refactor invalid nVMX state test to prepare for RSM testcase Sean Christopherson
@ 2026-07-28  0:51   ` sashiko-bot
  0 siblings, 0 replies; 10+ messages in thread
From: sashiko-bot @ 2026-07-28  0:51 UTC (permalink / raw)
  To: Sean Christopherson; +Cc: kvm

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] The assertion failure message in `vcpu_run_to_io` hardcodes 'L2' as the expected guest level, ignoring the `want_l2` parameter.
--

--- Patch [5]: [PATCH v4 5/6] KVM: selftests: Refactor invalid nVMX state test to prepare for RSM testcase ---
commit 7bfb34d63ffa2d4c98580f574a703358bca449c2
Author: Sean Christopherson <seanjc@google.com>

KVM: selftests: Refactor invalid nVMX state test to prepare for RSM testcase

This commit extracts the VM creation and initial vCPU execution logic into
helper functions to allow reuse for a future RSM testcase. It also eliminates
the global vm variable and adds an assertion to ensure L2 is never resumed
after an I/O exit to L1.

> 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
> --- a/tools/testing/selftests/kvm/x86/vmx_invalid_nested_guest_state.c
> +++ b/tools/testing/selftests/kvm/x86/vmx_invalid_nested_guest_state.c

[ ... ]

> +static void vcpu_run_to_io(struct kvm_vcpu *vcpu, bool want_l2)
>  {
> -	gva_t vmx_pages_gva;
> -	struct kvm_sregs sregs;
> -	struct kvm_vcpu *vcpu;
> -	struct kvm_run *run;
> -	struct ucall uc;
> +	struct kvm_run *run = vcpu->run;
>  
> -	TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX));
> +	vcpu_run(vcpu);
>  
> -	vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code);
> +	TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
>  
> -	/* Allocate VMX pages and shared descriptors (vmx_pages). */
> -	vcpu_alloc_vmx(vm, &vmx_pages_gva);
> -	vcpu_args_set(vcpu, 1, vmx_pages_gva);
> +	TEST_ASSERT(run->io.port == ARBITRARY_IO_PORT &&
> +		    (!!(run->flags & KVM_RUN_X86_GUEST_MODE) == want_l2  ||
> +		     !kvm_has_cap(KVM_CAP_X86_GUEST_MODE)),
> +		    "Expected IN from port %d from L2, got port %d from L%u",

[Severity: Low]
Is the hardcoded "from L2" in the expected message string intentional?

Since vcpu_run_to_io() is a helper that takes a want_l2 parameter, the error
output could be misleading if a test calls it expecting an exit from L1.
Would it make sense to dynamically print L1 or L2 in the failure message
based on the want_l2 parameter?

> +		    ARBITRARY_IO_PORT, run->io.port,
> +		    1 + !!(run->flags & KVM_RUN_X86_GUEST_MODE));
> +}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260728004351.887076-1-seanjc@google.com?part=5

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v4 1/6] KVM: x86: Extract VMX's unhandleable emulation check to common x86
  2026-07-28  0:43 ` [PATCH v4 1/6] KVM: x86: Extract VMX's unhandleable emulation check to common x86 Sean Christopherson
@ 2026-07-28  0:58   ` sashiko-bot
  2026-07-28  1:01     ` Sean Christopherson
  0 siblings, 1 reply; 10+ messages in thread
From: sashiko-bot @ 2026-07-28  0:58 UTC (permalink / raw)
  To: Sean Christopherson; +Cc: kvm

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] The mandatory `.vcpu_pre_run` hook is conditionally assigned `NULL` for VMX on non-TDX builds, causing a WARN on module load and breaking vCPU execution.
--

--- Patch [1]: [PATCH v4 1/6] KVM: x86: Extract VMX's unhandleable emulation check to common x86 ---
commit 1ae0061cc0844907be897cd2b6f318482c190f81
Author: Sean Christopherson <seanjc@google.com>

KVM: x86: Extract VMX's unhandleable emulation check to common x86

Expose VMX's check for unhandleable emulation as its own kvm_x86_ops hook,
and move the actual pre-KVM_RUN check into common x86.  This will allow
sharing the core logic with KVM's RSM emulation without needed to add a
post-RSM hook, and is a step towards removing the .vcpu_pre_run() hook
entirely.

> diff --git a/arch/x86/kvm/vmx/main.c b/arch/x86/kvm/vmx/main.c
> index 04f986e3d439a..924a629c21e2f 100644
> --- a/arch/x86/kvm/vmx/main.c
> +++ b/arch/x86/kvm/vmx/main.c
[ ... ]
> @@ -939,11 +949,12 @@ struct kvm_x86_ops vt_x86_ops __initdata = {
>  	.flush_tlb_gva = vt_op(flush_tlb_gva),
>  	.flush_tlb_guest = vt_op(flush_tlb_guest),
>  
> -	.vcpu_pre_run = vt_op(vcpu_pre_run),
> +	.vcpu_pre_run = vt_op_tdx_only(vcpu_pre_run),

[Severity: High]
Does this conditionally assigned NULL pointer cause a module load warning on
systems without CONFIG_KVM_INTEL_TDX enabled?

Since vcpu_pre_run is declared as a mandatory hook (KVM_X86_OP(vcpu_pre_run))
in arch/x86/include/asm/kvm-x86-ops.h, setting it to NULL when TDX is
disabled will trigger a WARN in kvm_ops_update().

[ ... ]

> diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
> index 960ff7fcd8dba..8c3efc1be1fa4 100644
> --- a/arch/x86/kvm/x86.c
> +++ b/arch/x86/kvm/x86.c
> @@ -8867,6 +8867,11 @@ static int kvm_x86_vcpu_pre_run(struct kvm_vcpu *vcpu)
>  	    !kvm_apic_init_sipi_allowed(vcpu))
>  		return -EINVAL;
>  
> +	if (kvm_x86_call(unhandleable_emulation_required)(vcpu)) {
> +		kvm_prepare_emulation_failure_exit(vcpu);
> +		return 0;
> +	}
> +
>  	return kvm_x86_call(vcpu_pre_run)(vcpu);

[Severity: High]
Could this break KVM_RUN by returning an uninitialized register value when TDX
is disabled?

When kvm_x86_vcpu_pre_run() calls this static call with a NULL target, the
static call machinery will patch the call site with a NOP or RET. The CPU will
execute the NOP and fall through, leaving the return value in the RAX register
unmodified.

Since the preceding call to unhandleable_emulation_required() is a boolean
function that typically returns false (setting AL=0), the uninitialized RAX
will evaluate as <= 0. This would cause kvm_arch_vcpu_ioctl_run() to
immediately exit to userspace instead of entering the guest.

>  }

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260728004351.887076-1-seanjc@google.com?part=1

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v4 1/6] KVM: x86: Extract VMX's unhandleable emulation check to common x86
  2026-07-28  0:58   ` sashiko-bot
@ 2026-07-28  1:01     ` Sean Christopherson
  0 siblings, 0 replies; 10+ messages in thread
From: Sean Christopherson @ 2026-07-28  1:01 UTC (permalink / raw)
  To: sashiko-reviews; +Cc: kvm

On Tue, Jul 28, 2026, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> > diff --git a/arch/x86/kvm/vmx/main.c b/arch/x86/kvm/vmx/main.c
> > index 04f986e3d439a..924a629c21e2f 100644
> > --- a/arch/x86/kvm/vmx/main.c
> > +++ b/arch/x86/kvm/vmx/main.c
> [ ... ]
> > @@ -939,11 +949,12 @@ struct kvm_x86_ops vt_x86_ops __initdata = {
> >  	.flush_tlb_gva = vt_op(flush_tlb_gva),
> >  	.flush_tlb_guest = vt_op(flush_tlb_guest),
> >  
> > -	.vcpu_pre_run = vt_op(vcpu_pre_run),
> > +	.vcpu_pre_run = vt_op_tdx_only(vcpu_pre_run),
> 
> [Severity: High]
> Does this conditionally assigned NULL pointer cause a module load warning on
> systems without CONFIG_KVM_INTEL_TDX enabled?
> 
> Since vcpu_pre_run is declared as a mandatory hook (KVM_X86_OP(vcpu_pre_run))
> in arch/x86/include/asm/kvm-x86-ops.h, setting it to NULL when TDX is
> disabled will trigger a WARN in kvm_ops_update().
> 
> [ ... ]
> 
> > diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
> > index 960ff7fcd8dba..8c3efc1be1fa4 100644
> > --- a/arch/x86/kvm/x86.c
> > +++ b/arch/x86/kvm/x86.c
> > @@ -8867,6 +8867,11 @@ static int kvm_x86_vcpu_pre_run(struct kvm_vcpu *vcpu)
> >  	    !kvm_apic_init_sipi_allowed(vcpu))
> >  		return -EINVAL;
> >  
> > +	if (kvm_x86_call(unhandleable_emulation_required)(vcpu)) {
> > +		kvm_prepare_emulation_failure_exit(vcpu);
> > +		return 0;
> > +	}
> > +
> >  	return kvm_x86_call(vcpu_pre_run)(vcpu);
> 
> [Severity: High]
> Could this break KVM_RUN by returning an uninitialized register value when TDX
> is disabled?
> 
> When kvm_x86_vcpu_pre_run() calls this static call with a NULL target, the
> static call machinery will patch the call site with a NOP or RET. The CPU will
> execute the NOP and fall through, leaving the return value in the RAX register
> unmodified.
> 
> Since the preceding call to unhandleable_emulation_required() is a boolean
> function that typically returns false (setting AL=0), the uninitialized RAX
> will evaluate as <= 0. This would cause kvm_arch_vcpu_ioctl_run() to
> immediately exit to userspace instead of entering the guest.

Gah, that's what I get for splitting the diff just before posting.  I'll fix
both of these in v2, and make sure to test patch 1 in isolation.

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2026-07-28  1:01 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-28  0:43 [PATCH v4 0/6] KVM: nVMX: Synthesize SHUTDOWN on RSM with bad state Sean Christopherson
2026-07-28  0:43 ` [PATCH v4 1/6] KVM: x86: Extract VMX's unhandleable emulation check to common x86 Sean Christopherson
2026-07-28  0:58   ` sashiko-bot
2026-07-28  1:01     ` Sean Christopherson
2026-07-28  0:43 ` [PATCH v4 2/6] KVM: nVMX: Synthesize SHUTDOWN on RSM if L2 requires emulation Sean Christopherson
2026-07-28  0:43 ` [PATCH v4 3/6] KVM: x86: Rework kvm_x86_ops.vcpu_pre_run() into .vcpu_needs_initialization() Sean Christopherson
2026-07-28  0:43 ` [PATCH v4 4/6] KVM: selftests: Use port 0x80 in invalid nVMX guest state test Sean Christopherson
2026-07-28  0:43 ` [PATCH v4 5/6] KVM: selftests: Refactor invalid nVMX state test to prepare for RSM testcase Sean Christopherson
2026-07-28  0:51   ` sashiko-bot
2026-07-28  0:43 ` [PATCH v4 6/6] KVM: selftests: Extend the invalid nVMX guest state test to cover RSM Sean Christopherson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox