[PATCH v16 19/51] KVM: x86: Don't emulate task switches when IBT or SHSTK is enabled

Sean Christopherson posted 51 patches 1 week, 5 days ago
[PATCH v16 19/51] KVM: x86: Don't emulate task switches when IBT or SHSTK is enabled
Posted by Sean Christopherson 1 week, 5 days ago
Exit to userspace with KVM_INTERNAL_ERROR_EMULATION if the guest triggers
task switch emulation with Indirect Branch Tracking or Shadow Stacks
enabled, as attempting to do the right thing would require non-trivial
effort and complexity, KVM doesn't support emulating CET generally, and
it's extremely unlikely that any guest will do task switches while also
utilizing CET.  Defer taking on the complexity until someone cares enough
to put in the time and effort to add support.

Per the SDM:

  If shadow stack is enabled, then the SSP of the task is located at the
  4 bytes at offset 104 in the 32-bit TSS and is used by the processor to
  establish the SSP when a task switch occurs from a task associated with
  this TSS. Note that the processor does not write the SSP of the task
  initiating the task switch to the TSS of that task, and instead the SSP
  of the previous task is pushed onto the shadow stack of the new task.

Note, per the SDM's pseudocode on TASK SWITCHING, IBT state for the new
privilege level is updated.  To keep things simple, check both S_CET and
U_CET (again, anyone that wants more precise checking can have the honor
of implementing support).

Reported-by: Binbin Wu <binbin.wu@linux.intel.com>
Closes: https://lore.kernel.org/all/819bd98b-2a60-4107-8e13-41f1e4c706b1@linux.intel.com
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
 arch/x86/kvm/x86.c | 35 ++++++++++++++++++++++++++++-------
 1 file changed, 28 insertions(+), 7 deletions(-)

diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index d2cccc7594d4..0c060e506f9d 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -12178,6 +12178,25 @@ int kvm_task_switch(struct kvm_vcpu *vcpu, u16 tss_selector, int idt_index,
 	struct x86_emulate_ctxt *ctxt = vcpu->arch.emulate_ctxt;
 	int ret;
 
+	if (kvm_is_cr4_bit_set(vcpu, X86_CR4_CET)) {
+		u64 u_cet, s_cet;
+
+		/*
+		 * Check both User and Supervisor on task switches as inter-
+		 * privilege level task switches are impacted by CET at both
+		 * the current privilege level and the new privilege level, and
+		 * that information is not known at this time.  The expectation
+		 * is that the guest won't require emulation of task switches
+		 * while using IBT or Shadow Stacks.
+		 */
+		if (__kvm_emulate_msr_read(vcpu, MSR_IA32_U_CET, &u_cet) ||
+		    __kvm_emulate_msr_read(vcpu, MSR_IA32_S_CET, &s_cet))
+			return EMULATION_FAILED;
+
+		if ((u_cet | s_cet) & CET_SHSTK_EN)
+			goto unhandled_task_switch;
+	}
+
 	init_emulate_ctxt(vcpu);
 
 	ret = emulator_task_switch(ctxt, tss_selector, idt_index, reason,
@@ -12187,17 +12206,19 @@ int kvm_task_switch(struct kvm_vcpu *vcpu, u16 tss_selector, int idt_index,
 	 * Report an error userspace if MMIO is needed, as KVM doesn't support
 	 * MMIO during a task switch (or any other complex operation).
 	 */
-	if (ret || vcpu->mmio_needed) {
-		vcpu->mmio_needed = false;
-		vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
-		vcpu->run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION;
-		vcpu->run->internal.ndata = 0;
-		return 0;
-	}
+	if (ret || vcpu->mmio_needed)
+		goto unhandled_task_switch;
 
 	kvm_rip_write(vcpu, ctxt->eip);
 	kvm_set_rflags(vcpu, ctxt->eflags);
 	return 1;
+
+unhandled_task_switch:
+	vcpu->mmio_needed = false;
+	vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
+	vcpu->run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION;
+	vcpu->run->internal.ndata = 0;
+	return 0;
 }
 EXPORT_SYMBOL_GPL(kvm_task_switch);
 
-- 
2.51.0.470.ga7dc726c21-goog
Re: [PATCH v16 19/51] KVM: x86: Don't emulate task switches when IBT or SHSTK is enabled
Posted by Chao Gao 1 week, 2 days ago
>@@ -12178,6 +12178,25 @@ int kvm_task_switch(struct kvm_vcpu *vcpu, u16 tss_selector, int idt_index,
> 	struct x86_emulate_ctxt *ctxt = vcpu->arch.emulate_ctxt;
> 	int ret;
> 
>+	if (kvm_is_cr4_bit_set(vcpu, X86_CR4_CET)) {
>+		u64 u_cet, s_cet;
>+
>+		/*
>+		 * Check both User and Supervisor on task switches as inter-
>+		 * privilege level task switches are impacted by CET at both
>+		 * the current privilege level and the new privilege level, and
>+		 * that information is not known at this time.  The expectation
>+		 * is that the guest won't require emulation of task switches
>+		 * while using IBT or Shadow Stacks.
>+		 */
>+		if (__kvm_emulate_msr_read(vcpu, MSR_IA32_U_CET, &u_cet) ||
>+		    __kvm_emulate_msr_read(vcpu, MSR_IA32_S_CET, &s_cet))
>+			return EMULATION_FAILED;

is it ok to return EMULATION_FAILED (-1) here?

It looks like this error code will be propagated to userspace and be
interpreted as -EPERM.
Re: [PATCH v16 19/51] KVM: x86: Don't emulate task switches when IBT or SHSTK is enabled
Posted by Binbin Wu 1 week, 2 days ago

On 9/20/2025 6:32 AM, Sean Christopherson wrote:
> Exit to userspace with KVM_INTERNAL_ERROR_EMULATION if the guest triggers
> task switch emulation with Indirect Branch Tracking or Shadow Stacks
> enabled,

The code just does it when shadow stack is enabled.

> as attempting to do the right thing would require non-trivial
> effort and complexity, KVM doesn't support emulating CET generally, and
> it's extremely unlikely that any guest will do task switches while also
> utilizing CET.  Defer taking on the complexity until someone cares enough
> to put in the time and effort to add support.
>
> Per the SDM:
>
>    If shadow stack is enabled, then the SSP of the task is located at the
>    4 bytes at offset 104 in the 32-bit TSS and is used by the processor to
>    establish the SSP when a task switch occurs from a task associated with
>    this TSS. Note that the processor does not write the SSP of the task
>    initiating the task switch to the TSS of that task, and instead the SSP
>    of the previous task is pushed onto the shadow stack of the new task.
>
> Note, per the SDM's pseudocode on TASK SWITCHING, IBT state for the new
> privilege level is updated.  To keep things simple, check both S_CET and
> U_CET (again, anyone that wants more precise checking can have the honor
> of implementing support).
>
> Reported-by: Binbin Wu <binbin.wu@linux.intel.com>
> Closes: https://lore.kernel.org/all/819bd98b-2a60-4107-8e13-41f1e4c706b1@linux.intel.com
> Signed-off-by: Sean Christopherson <seanjc@google.com>
> ---
>   arch/x86/kvm/x86.c | 35 ++++++++++++++++++++++++++++-------
>   1 file changed, 28 insertions(+), 7 deletions(-)
>
> diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
> index d2cccc7594d4..0c060e506f9d 100644
> --- a/arch/x86/kvm/x86.c
> +++ b/arch/x86/kvm/x86.c
> @@ -12178,6 +12178,25 @@ int kvm_task_switch(struct kvm_vcpu *vcpu, u16 tss_selector, int idt_index,
>   	struct x86_emulate_ctxt *ctxt = vcpu->arch.emulate_ctxt;
>   	int ret;
>   
> +	if (kvm_is_cr4_bit_set(vcpu, X86_CR4_CET)) {
> +		u64 u_cet, s_cet;
> +
> +		/*
> +		 * Check both User and Supervisor on task switches as inter-
> +		 * privilege level task switches are impacted by CET at both
> +		 * the current privilege level and the new privilege level, and
> +		 * that information is not known at this time.  The expectation
> +		 * is that the guest won't require emulation of task switches
> +		 * while using IBT or Shadow Stacks.
> +		 */
> +		if (__kvm_emulate_msr_read(vcpu, MSR_IA32_U_CET, &u_cet) ||
> +		    __kvm_emulate_msr_read(vcpu, MSR_IA32_S_CET, &s_cet))
> +			return EMULATION_FAILED;
> +
> +		if ((u_cet | s_cet) & CET_SHSTK_EN)
> +			goto unhandled_task_switch;
> +	}
> +
>   	init_emulate_ctxt(vcpu);
>   
>   	ret = emulator_task_switch(ctxt, tss_selector, idt_index, reason,
> @@ -12187,17 +12206,19 @@ int kvm_task_switch(struct kvm_vcpu *vcpu, u16 tss_selector, int idt_index,
>   	 * Report an error userspace if MMIO is needed, as KVM doesn't support
>   	 * MMIO during a task switch (or any other complex operation).
>   	 */
> -	if (ret || vcpu->mmio_needed) {
> -		vcpu->mmio_needed = false;
> -		vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
> -		vcpu->run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION;
> -		vcpu->run->internal.ndata = 0;
> -		return 0;
> -	}
> +	if (ret || vcpu->mmio_needed)
> +		goto unhandled_task_switch;
>   
>   	kvm_rip_write(vcpu, ctxt->eip);
>   	kvm_set_rflags(vcpu, ctxt->eflags);
>   	return 1;
> +
> +unhandled_task_switch:
> +	vcpu->mmio_needed = false;
> +	vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
> +	vcpu->run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION;
> +	vcpu->run->internal.ndata = 0;
> +	return 0;
>   }
>   EXPORT_SYMBOL_GPL(kvm_task_switch);
>
Re: [PATCH v16 19/51] KVM: x86: Don't emulate task switches when IBT or SHSTK is enabled
Posted by Sean Christopherson 1 week, 2 days ago
On Mon, Sep 22, 2025, Binbin Wu wrote:
> 
> 
> On 9/20/2025 6:32 AM, Sean Christopherson wrote:
> > Exit to userspace with KVM_INTERNAL_ERROR_EMULATION if the guest triggers
> > task switch emulation with Indirect Branch Tracking or Shadow Stacks
> > enabled,
> 
> The code just does it when shadow stack is enabled.

Doh.  Fixed that and the EMULATION_FAILED typo Chao pointed out:

diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 8b31dfcb1de9..06a88a2b08d7 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -12194,9 +12194,9 @@ int kvm_task_switch(struct kvm_vcpu *vcpu, u16 tss_selector, int idt_index,
                 */
                if (__kvm_emulate_msr_read(vcpu, MSR_IA32_U_CET, &u_cet) ||
                    __kvm_emulate_msr_read(vcpu, MSR_IA32_S_CET, &s_cet))
-                       return EMULATION_FAILED;
+                       goto unhandled_task_switch;
 
-               if ((u_cet | s_cet) & CET_SHSTK_EN)
+               if ((u_cet | s_cet) & (CET_ENDBR_EN | CET_SHSTK_EN))
                        goto unhandled_task_switch;
        }
Re: [PATCH v16 19/51] KVM: x86: Don't emulate task switches when IBT or SHSTK is enabled
Posted by Xiaoyao Li 1 week, 1 day ago
On 9/23/2025 1:23 AM, Sean Christopherson wrote:
> On Mon, Sep 22, 2025, Binbin Wu wrote:
>>
>>
>> On 9/20/2025 6:32 AM, Sean Christopherson wrote:
>>> Exit to userspace with KVM_INTERNAL_ERROR_EMULATION if the guest triggers
>>> task switch emulation with Indirect Branch Tracking or Shadow Stacks
>>> enabled,
>>
>> The code just does it when shadow stack is enabled.
> 
> Doh.  Fixed that and the EMULATION_FAILED typo Chao pointed out:
> 
> diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
> index 8b31dfcb1de9..06a88a2b08d7 100644
> --- a/arch/x86/kvm/x86.c
> +++ b/arch/x86/kvm/x86.c
> @@ -12194,9 +12194,9 @@ int kvm_task_switch(struct kvm_vcpu *vcpu, u16 tss_selector, int idt_index,
>                   */
>                  if (__kvm_emulate_msr_read(vcpu, MSR_IA32_U_CET, &u_cet) ||
>                      __kvm_emulate_msr_read(vcpu, MSR_IA32_S_CET, &s_cet))
> -                       return EMULATION_FAILED;
> +                       goto unhandled_task_switch;
>   
> -               if ((u_cet | s_cet) & CET_SHSTK_EN)
> +               if ((u_cet | s_cet) & (CET_ENDBR_EN | CET_SHSTK_EN))
>                          goto unhandled_task_switch;
>          }

Reviewed-by: Xiaoyao Li <xiaoyao.li@intel.com>