FAILED: patch "[PATCH] KVM: VMX: Inject #UD if guest tries to execute SEAMCALL or" failed to apply to 6.17-stable tree
Subsystems (3)
[email protected]
maintainerThe patch below does not apply to the 6.17-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.17.y
git checkout FETCH_HEAD
git cherry-pick -x 9d7dfb95da2cb5c1287df2f3468bcb70d8b31087
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2025112027-ranch-retool-efaa@gregkh' --subject-prefix 'PATCH 6.17.y' HEAD^..
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 9d7dfb95da2cb5c1287df2f3468bcb70d8b31087 Mon Sep 17 00:00:00 2001
From: Sean Christopherson <[email protected]>
Date: Thu, 16 Oct 2025 11:21:47 -0700
Subject: [PATCH] KVM: VMX: Inject #UD if guest tries to execute SEAMCALL or
TDCALL
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Add VMX exit handlers for SEAMCALL and TDCALL to inject a #UD if a non-TD guest attempts to execute SEAMCALL or TDCALL. Neither SEAMCALL nor TDCALL is gated by any software enablement other than VMXON, and so will generate a VM-Exit instead of e.g. a native #UD when executed from the guest kernel.
Note! No unprivileged DoS of the L1 kernel is possible as TDCALL and SEAMCALL #GP at CPL > 0, and the CPL check is performed prior to the VMX non-root (VM-Exit) check, i.e. userspace can't crash the VM. And for a nested guest, KVM forwards unknown exits to L1, i.e. an L2 kernel can crash itself, but not L1.
Note #2! The Intel® Trust Domain CPU Architectural Extensions spec's pseudocode shows the CPL > 0 check for SEAMCALL coming _after_ the VM-Exit, but that appears to be a documentation bug (likely because the CPL > 0 check was incorrectly bundled with other lower-priority #GP checks). Testing on SPR and EMR shows that the CPL > 0 check is performed before the VMX non-root check, i.e. SEAMCALL #GPs when executed in usermode.
Note #3! The aforementioned Trust Domain spec uses confusing pseudocode that says that SEAMCALL will #UD if executed "inSEAM", but "inSEAM" specifically means in SEAM Root Mode, i.e. in the TDX-Module. The long- form description explicitly states that SEAMCALL generates an exit when executed in "SEAM VMX non-root operation". But that's a moot point as the TDX-Module injects #UD if the guest attempts to execute SEAMCALL, as documented in the "Unconditionally Blocked Instructions" section of the TDX-Module base specification.
arch/x86/include/uapi/asm/vmx.h +1 -0
@@ -93,6 +93,7 @@#define EXIT_REASON_TPAUSE 68#define EXIT_REASON_BUS_LOCK 74#define EXIT_REASON_NOTIFY 75 +#define EXIT_REASON_SEAMCALL 76#define EXIT_REASON_TDCALL 77#define EXIT_REASON_MSR_READ_IMM 84#define EXIT_REASON_MSR_WRITE_IMM 85
arch/x86/kvm/vmx/nested.c +8 -0
@@ -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. +*/ +return false;default :return true; }
arch/x86/kvm/vmx/vmx.c +8 -0
@@ -6032,6 +6032,12 @@ static int handle_vmx_instruction(struct kvm_vcpu *vcpu)return 1 ; } +static int handle_tdx_instruction (struct kvm_vcpu *vcpu) +{ +kvm_queue_exception (vcpu,UD_VECTOR ); +return 1 ; +} +#ifndef CONFIG_X86_SGX_KVM static int handle_encls (struct kvm_vcpu *vcpu) {
@@ -6157,6 +6163,8 @@ static int (*kvm_vmx_exit_handlers[])(struct kvm_vcpu *vcpu) = { [EXIT_REASON_ENCLS] = handle_encls, [EXIT_REASON_BUS_LOCK] = handle_bus_lock_vmexit, [EXIT_REASON_NOTIFY] = handle_notify, + [EXIT_REASON_SEAMCALL] = handle_tdx_instruction, + [EXIT_REASON_TDCALL] = handle_tdx_instruction, [EXIT_REASON_MSR_READ_IMM] = handle_rdmsr_imm, [EXIT_REASON_MSR_WRITE_IMM] = handle_wrmsr_imm, };
Sasha Levin
maintainerPATCH 1/3 KVM: x86: Rename local "ecx" variables to "msr" and "pmc" as appropriate
From: Sean Christopherson <[email protected]>
[ Upstream commit ec400f6c2f2703cb6c698dd00b28cfdb8ee5cdcc ]
Rename "ecx" variables in {RD,WR}MSR and RDPMC helpers to "msr" and "pmc" respectively, in anticipation of adding support for the immediate variants of RDMSR and WRMSRNS, and to better document what the variables hold (versus where the data originated).
No functional change intended.
Stable-dep-of: 9d7dfb95da2c ("KVM: VMX: Inject #UD if guest tries to execute SEAMCALL or TDCALL")
--- arch/x86/kvm/x86.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-)
arch/x86/kvm/x86.c +12 -12
@@ -1579,10 +1579,10 @@ EXPORT_SYMBOL_GPL(kvm_get_dr);int kvm_emulate_rdpmc (struct kvm_vcpu *vcpu) { -u32 ecx =kvm_rcx_read (vcpu); +u32 pmc =kvm_rcx_read (vcpu);u64 data; -if (kvm_pmu_rdpmc (vcpu, ecx, &data)) { +if (kvm_pmu_rdpmc (vcpu, pmc, &data)) {kvm_inject_gp (vcpu,0 );return 1 ; }
@@ -2033,23 +2033,23 @@ static int kvm_msr_user_space(struct kvm_vcpu *vcpu, u32 index,int kvm_emulate_rdmsr (struct kvm_vcpu *vcpu) { -u32 ecx =kvm_rcx_read (vcpu); +u32 msr =kvm_rcx_read (vcpu);u64 data;int r; - r =kvm_get_msr_with_filter (vcpu, ecx, &data); + r =kvm_get_msr_with_filter (vcpu, msr, &data);if (!r) { -trace_kvm_msr_read (ecx, data); +trace_kvm_msr_read (msr, data);kvm_rax_write (vcpu, data &-1u );kvm_rdx_write (vcpu, (data >>32 ) &-1u ); }else {/* MSR read failed? See if we should ask user space */ -if (kvm_msr_user_space (vcpu, ecx,KVM_EXIT_X86_RDMSR ,0 , +if (kvm_msr_user_space (vcpu, msr,KVM_EXIT_X86_RDMSR ,0 , complete_fast_rdmsr, r))return 0 ; -trace_kvm_msr_read_ex (ecx); +trace_kvm_msr_read_ex (msr); }return kvm_x86_call (complete_emulated_msr)(vcpu, r);
@@ -2058,23 +2058,23 @@ EXPORT_SYMBOL_GPL(kvm_emulate_rdmsr);int kvm_emulate_wrmsr (struct kvm_vcpu *vcpu) { -u32 ecx =kvm_rcx_read (vcpu); +u32 msr =kvm_rcx_read (vcpu);u64 data =kvm_read_edx_eax (vcpu);int r; - r =kvm_set_msr_with_filter (vcpu, ecx, data); + r =kvm_set_msr_with_filter (vcpu, msr, data);if (!r) { -trace_kvm_msr_write (ecx, data); +trace_kvm_msr_write (msr, data); }else {/* MSR write failed? See if we should ask user space */ -if (kvm_msr_user_space (vcpu, ecx,KVM_EXIT_X86_WRMSR , data, +if (kvm_msr_user_space (vcpu, msr,KVM_EXIT_X86_WRMSR , data, complete_fast_msr_access, r))return 0 ;/* Signal all other negative errors to userspace */ if (r <0 )return r; -trace_kvm_msr_write_ex (ecx, data); +trace_kvm_msr_write_ex (msr, data); }return kvm_x86_call (complete_emulated_msr)(vcpu, r);
Signature · 1 line
2.51.0
Sasha Levin
maintainerPATCH 2/3 KVM: x86: Add support for RDMSR/WRMSRNS w/ immediate on Intel
From: Xin Li <[email protected]>
[ Upstream commit 885df2d2109a60f84d84639ce6d95a91045f6c45 ]
Add support for the immediate forms of RDMSR and WRMSRNS (currently Intel-only). The immediate variants are only valid in 64-bit mode, and use a single general purpose register for the data (the register is also encoded in the instruction, i.e. not implicit like regular RDMSR/WRMSR).
The immediate variants are primarily motivated by performance, not code
size: by having the MSR index in an immediate, it is available *much* earlier in the CPU pipeline, which allows hardware much more leeway about how a particular MSR is handled.
Intel VMX support for the immediate forms of MSR accesses communicates exit information to the host as follows:
1) The immediate form of RDMSR uses VM-Exit Reason 84.
2) The immediate form of WRMSRNS uses VM-Exit Reason 85.
3) For both VM-Exit reasons 84 and 85, the Exit Qualification field is set to the MSR index that triggered the VM-Exit.
4) Bits 3 ~ 6 of the VM-Exit Instruction Information field are set to the register encoding used by the immediate form of the instruction, i.e. the destination register for RDMSR, and the source for WRMSRNS.
5) The VM-Exit Instruction Length field records the size of the immediate form of the MSR instruction.
To deal with userspace RDMSR exits, stash the destination register in a new kvm_vcpu_arch field, similar to cui_linear_rip, pio, etc. Alternatively, the register could be saved in kvm_run.msr or re-retrieved from the VMCS, but the former would require sanitizing the value to ensure userspace doesn't clobber the value to an out-of-bounds index, and the latter would require a new one-off kvm_x86_ops hook.
Don't bother adding support for the instructions in KVM's emulator, as the only way for RDMSR/WRMSR to be encountered is if KVM is emulating large swaths of code due to invalid guest state, and a vCPU cannot have invalid guest state while in 64-bit mode.
Stable-dep-of: 9d7dfb95da2c ("KVM: VMX: Inject #UD if guest tries to execute SEAMCALL or TDCALL")
--- arch/x86/include/asm/kvm_host.h | 3 ++ arch/x86/include/uapi/asm/vmx.h | 6 +++- arch/x86/kvm/vmx/nested.c | 13 ++++++-- arch/x86/kvm/vmx/vmx.c | 21 +++++++++++++ arch/x86/kvm/vmx/vmx.h | 5 +++ arch/x86/kvm/x86.c | 55 +++++++++++++++++++++++++++------ 6 files changed, 90 insertions(+), 13 deletions(-)
arch/x86/include/asm/kvm_host.h +3 -0
@@ -926,6 +926,7 @@ struct kvm_vcpu_arch {bool emulate_regs_need_sync_from_vcpu;int (*complete_userspace_io)(struct kvm_vcpu *vcpu);unsigned long cui_linear_rip; +int cui_rdmsr_imm_reg;gpa_t time;s8 pvclock_tsc_shift;
@@ -2155,7 +2156,9 @@ int __kvm_get_msr(struct kvm_vcpu *vcpu, u32 index, u64 *data, bool host_initiatint kvm_get_msr (struct kvm_vcpu *vcpu,u32 index,u64 *data);int kvm_set_msr (struct kvm_vcpu *vcpu,u32 index,u64 data);int kvm_emulate_rdmsr (struct kvm_vcpu *vcpu); +int kvm_emulate_rdmsr_imm (struct kvm_vcpu *vcpu,u32 msr,int reg);int kvm_emulate_wrmsr (struct kvm_vcpu *vcpu); +int kvm_emulate_wrmsr_imm (struct kvm_vcpu *vcpu,u32 msr,int reg);int kvm_emulate_as_nop (struct kvm_vcpu *vcpu);int kvm_emulate_invd (struct kvm_vcpu *vcpu);int kvm_emulate_mwait (struct kvm_vcpu *vcpu);
arch/x86/include/uapi/asm/vmx.h +5 -1
@@ -94,6 +94,8 @@#define EXIT_REASON_BUS_LOCK 74#define EXIT_REASON_NOTIFY 75#define EXIT_REASON_TDCALL 77 +#define EXIT_REASON_MSR_READ_IMM 84 +#define EXIT_REASON_MSR_WRITE_IMM 85#define VMX_EXIT_REASONS \ { EXIT_REASON_EXCEPTION_NMI, "EXCEPTION_NMI" }, \
@@ -158,7 +160,9 @@ { EXIT_REASON_TPAUSE, "TPAUSE" }, \ { EXIT_REASON_BUS_LOCK, "BUS_LOCK" }, \ { EXIT_REASON_NOTIFY, "NOTIFY" }, \ - { EXIT_REASON_TDCALL, "TDCALL" } + { EXIT_REASON_TDCALL, "TDCALL" }, \ + { EXIT_REASON_MSR_READ_IMM, "MSR_READ_IMM" }, \ + { EXIT_REASON_MSR_WRITE_IMM, "MSR_WRITE_IMM" } #define VMX_EXIT_REASON_FLAGS \ { VMX_EXIT_REASONS_FAILED_VMENTRY, "FAILED_VMENTRY" }
arch/x86/kvm/vmx/nested.c +11 -2
@@ -6216,19 +6216,26 @@ static bool nested_vmx_exit_handled_msr(struct kvm_vcpu *vcpu,struct vmcs12 *vmcs12, union vmx_exit_reason exit_reason) { - u32 msr_index = kvm_rcx_read(vcpu); + u32 msr_index;gpa_t bitmap;if (!nested_cpu_has (vmcs12,CPU_BASED_USE_MSR_BITMAPS ))return true; +if (exit_reason.basic ==EXIT_REASON_MSR_READ_IMM || + exit_reason.basic ==EXIT_REASON_MSR_WRITE_IMM ) + msr_index =vmx_get_exit_qual (vcpu); +else + msr_index =kvm_rcx_read (vcpu); +/* * The MSR_BITMAP page is divided into four 1024-byte bitmaps, * for the four combinations of read/write and low/high MSR numbers. * First we need to figure out which of the four to use: */ bitmap = vmcs12->msr_bitmap; -if (exit_reason.basic ==EXIT_REASON_MSR_WRITE ) +if (exit_reason.basic ==EXIT_REASON_MSR_WRITE || + exit_reason.basic ==EXIT_REASON_MSR_WRITE_IMM ) bitmap +=2048 ;if (msr_index >=0xc0000000 ) { msr_index -=0xc0000000 ;
@@ -6527,6 +6534,8 @@ static bool nested_vmx_l1_wants_exit(struct kvm_vcpu *vcpu,return nested_cpu_has2 (vmcs12,SECONDARY_EXEC_DESC );case EXIT_REASON_MSR_READ :case EXIT_REASON_MSR_WRITE : +case EXIT_REASON_MSR_READ_IMM : +case EXIT_REASON_MSR_WRITE_IMM :return nested_vmx_exit_handled_msr (vcpu, vmcs12, exit_reason);case EXIT_REASON_INVALID_STATE :return true;
arch/x86/kvm/vmx/vmx.c +21 -0
@@ -6003,6 +6003,23 @@ static int handle_notify(struct kvm_vcpu *vcpu)return 1 ; } +static int vmx_get_msr_imm_reg (struct kvm_vcpu *vcpu) +{ +return vmx_get_instr_info_reg (vmcs_read32 (VMX_INSTRUCTION_INFO )); +} + +static int handle_rdmsr_imm (struct kvm_vcpu *vcpu) +{ +return kvm_emulate_rdmsr_imm (vcpu,vmx_get_exit_qual (vcpu), +vmx_get_msr_imm_reg (vcpu)); +} + +static int handle_wrmsr_imm (struct kvm_vcpu *vcpu) +{ +return kvm_emulate_wrmsr_imm (vcpu,vmx_get_exit_qual (vcpu), +vmx_get_msr_imm_reg (vcpu)); +} +/* * The exit handlers return 1 if the exit was handled fully and guest execution * may resume. Otherwise they set the kvm_run parameter to indicate what needs
@@ -6061,6 +6078,8 @@ static int (*kvm_vmx_exit_handlers[])(struct kvm_vcpu *vcpu) = { [EXIT_REASON_ENCLS] = handle_encls, [EXIT_REASON_BUS_LOCK] = handle_bus_lock_vmexit, [EXIT_REASON_NOTIFY] = handle_notify, + [EXIT_REASON_MSR_READ_IMM] = handle_rdmsr_imm, + [EXIT_REASON_MSR_WRITE_IMM] = handle_wrmsr_imm, }; static const int kvm_vmx_max_exit_handlers =
@@ -6495,6 +6514,8 @@ static int __vmx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t exit_fastpath)#ifdef CONFIG_MITIGATION_RETPOLINE if (exit_reason.basic ==EXIT_REASON_MSR_WRITE )return kvm_emulate_wrmsr (vcpu); +else if (exit_reason.basic ==EXIT_REASON_MSR_WRITE_IMM ) +return handle_wrmsr_imm (vcpu);else if (exit_reason.basic ==EXIT_REASON_PREEMPTION_TIMER )return handle_preemption_timer (vcpu);else if (exit_reason.basic ==EXIT_REASON_INTERRUPT_WINDOW )
arch/x86/kvm/vmx/vmx.h +5 -0
@@ -706,6 +706,11 @@ static inline bool vmx_guest_state_valid(struct kvm_vcpu *vcpu)void dump_vmcs (struct kvm_vcpu *vcpu); +static inline int vmx_get_instr_info_reg (u32 vmx_instr_info) +{ +return (vmx_instr_info >>3 ) &0xf ; +} +static inline int vmx_get_instr_info_reg2 (u32 vmx_instr_info) {return (vmx_instr_info >>28 ) &0xf ;
arch/x86/kvm/x86.c +45 -10
98 lines not on this page · raw message
Signature · 1 line
2.51.0
Sasha Levin
maintainerPATCH 3/3 KVM: VMX: Inject #UD if guest tries to execute SEAMCALL or TDCALL
In reply to Sasha LevinFrom: Sean Christopherson <[email protected]>
[ Upstream commit 9d7dfb95da2cb5c1287df2f3468bcb70d8b31087 ]
Add VMX exit handlers for SEAMCALL and TDCALL to inject a #UD if a non-TD guest attempts to execute SEAMCALL or TDCALL. Neither SEAMCALL nor TDCALL is gated by any software enablement other than VMXON, and so will generate a VM-Exit instead of e.g. a native #UD when executed from the guest kernel.
Note! No unprivileged DoS of the L1 kernel is possible as TDCALL and SEAMCALL #GP at CPL > 0, and the CPL check is performed prior to the VMX non-root (VM-Exit) check, i.e. userspace can't crash the VM. And for a nested guest, KVM forwards unknown exits to L1, i.e. an L2 kernel can crash itself, but not L1.
Note #2! The Intel® Trust Domain CPU Architectural Extensions spec's pseudocode shows the CPL > 0 check for SEAMCALL coming _after_ the VM-Exit, but that appears to be a documentation bug (likely because the CPL > 0 check was incorrectly bundled with other lower-priority #GP checks). Testing on SPR and EMR shows that the CPL > 0 check is performed before the VMX non-root check, i.e. SEAMCALL #GPs when executed in usermode.
Note #3! The aforementioned Trust Domain spec uses confusing pseudocode that says that SEAMCALL will #UD if executed "inSEAM", but "inSEAM" specifically means in SEAM Root Mode, i.e. in the TDX-Module. The long- form description explicitly states that SEAMCALL generates an exit when executed in "SEAM VMX non-root operation". But that's a moot point as the TDX-Module injects #UD if the guest attempts to execute SEAMCALL, as documented in the "Unconditionally Blocked Instructions" section of the TDX-Module base specification.
--- arch/x86/include/uapi/asm/vmx.h | 1 + arch/x86/kvm/vmx/nested.c | 8 ++++++++ arch/x86/kvm/vmx/vmx.c | 8 ++++++++ 3 files changed, 17 insertions(+)
arch/x86/include/uapi/asm/vmx.h +1 -0
@@ -93,6 +93,7 @@#define EXIT_REASON_TPAUSE 68#define EXIT_REASON_BUS_LOCK 74#define EXIT_REASON_NOTIFY 75 +#define EXIT_REASON_SEAMCALL 76#define EXIT_REASON_TDCALL 77#define EXIT_REASON_MSR_READ_IMM 84#define EXIT_REASON_MSR_WRITE_IMM 85
arch/x86/kvm/vmx/nested.c +8 -0
@@ -6587,6 +6587,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. +*/ +return false;default :return true; }
arch/x86/kvm/vmx/vmx.c +8 -0
@@ -5953,6 +5953,12 @@ static int handle_vmx_instruction(struct kvm_vcpu *vcpu)return 1 ; } +static int handle_tdx_instruction (struct kvm_vcpu *vcpu) +{ +kvm_queue_exception (vcpu,UD_VECTOR ); +return 1 ; +} +#ifndef CONFIG_X86_SGX_KVM static int handle_encls (struct kvm_vcpu *vcpu) {
@@ -6078,6 +6084,8 @@ static int (*kvm_vmx_exit_handlers[])(struct kvm_vcpu *vcpu) = { [EXIT_REASON_ENCLS] = handle_encls, [EXIT_REASON_BUS_LOCK] = handle_bus_lock_vmexit, [EXIT_REASON_NOTIFY] = handle_notify, + [EXIT_REASON_SEAMCALL] = handle_tdx_instruction, + [EXIT_REASON_TDCALL] = handle_tdx_instruction, [EXIT_REASON_MSR_READ_IMM] = handle_rdmsr_imm, [EXIT_REASON_MSR_WRITE_IMM] = handle_wrmsr_imm, };
Signature · 1 line
2.51.0