Skip to content

Commit fc02735

Browse files
jpoimboesuryasaimadhu
authored andcommitted
KVM: VMX: Prevent guest RSB poisoning attacks with eIBRS
On eIBRS systems, the returns in the vmexit return path from __vmx_vcpu_run() to vmx_vcpu_run() are exposed to RSB poisoning attacks. Fix that by moving the post-vmexit spec_ctrl handling to immediately after the vmexit. Signed-off-by: Josh Poimboeuf <[email protected]> Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Signed-off-by: Borislav Petkov <[email protected]>
1 parent bb06650 commit fc02735

File tree

6 files changed

+73
-31
lines changed

6 files changed

+73
-31
lines changed

arch/x86/include/asm/nospec-branch.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ static inline void indirect_branch_prediction_barrier(void)
274274

275275
/* The Intel SPEC CTRL MSR base value cache */
276276
extern u64 x86_spec_ctrl_base;
277+
extern u64 x86_spec_ctrl_current;
277278
extern void write_spec_ctrl_current(u64 val, bool force);
278279
extern u64 spec_ctrl_current(void);
279280

arch/x86/kernel/cpu/bugs.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,10 @@ void __init check_bugs(void)
195195
#endif
196196
}
197197

198+
/*
199+
* NOTE: For VMX, this function is not called in the vmexit path.
200+
* It uses vmx_spec_ctrl_restore_host() instead.
201+
*/
198202
void
199203
x86_virt_spec_ctrl(u64 guest_spec_ctrl, u64 guest_virt_spec_ctrl, bool setguest)
200204
{

arch/x86/kvm/vmx/run_flags.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@
33
#define __KVM_X86_VMX_RUN_FLAGS_H
44

55
#define VMX_RUN_VMRESUME (1 << 0)
6+
#define VMX_RUN_SAVE_SPEC_CTRL (1 << 1)
67

78
#endif /* __KVM_X86_VMX_RUN_FLAGS_H */

arch/x86/kvm/vmx/vmenter.S

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,10 @@
3333

3434
/**
3535
* __vmx_vcpu_run - Run a vCPU via a transition to VMX guest mode
36-
* @vmx: struct vcpu_vmx * (forwarded to vmx_update_host_rsp)
36+
* @vmx: struct vcpu_vmx *
3737
* @regs: unsigned long * (to guest registers)
38-
* @flags: VMX_RUN_VMRESUME: use VMRESUME instead of VMLAUNCH
38+
* @flags: VMX_RUN_VMRESUME: use VMRESUME instead of VMLAUNCH
39+
* VMX_RUN_SAVE_SPEC_CTRL: save guest SPEC_CTRL into vmx->spec_ctrl
3940
*
4041
* Returns:
4142
* 0 on VM-Exit, 1 on VM-Fail
@@ -54,6 +55,12 @@ SYM_FUNC_START(__vmx_vcpu_run)
5455
#endif
5556
push %_ASM_BX
5657

58+
/* Save @vmx for SPEC_CTRL handling */
59+
push %_ASM_ARG1
60+
61+
/* Save @flags for SPEC_CTRL handling */
62+
push %_ASM_ARG3
63+
5764
/*
5865
* Save @regs, _ASM_ARG2 may be modified by vmx_update_host_rsp() and
5966
* @regs is needed after VM-Exit to save the guest's register values.
@@ -149,25 +156,23 @@ SYM_INNER_LABEL(vmx_vmexit, SYM_L_GLOBAL)
149156
mov %r15, VCPU_R15(%_ASM_AX)
150157
#endif
151158

152-
/* IMPORTANT: RSB must be stuffed before the first return. */
153-
FILL_RETURN_BUFFER %_ASM_BX, RSB_CLEAR_LOOPS, X86_FEATURE_RETPOLINE
154-
155-
/* Clear RAX to indicate VM-Exit (as opposed to VM-Fail). */
156-
xor %eax, %eax
159+
/* Clear return value to indicate VM-Exit (as opposed to VM-Fail). */
160+
xor %ebx, %ebx
157161

158162
.Lclear_regs:
159163
/*
160-
* Clear all general purpose registers except RSP and RAX to prevent
164+
* Clear all general purpose registers except RSP and RBX to prevent
161165
* speculative use of the guest's values, even those that are reloaded
162166
* via the stack. In theory, an L1 cache miss when restoring registers
163167
* could lead to speculative execution with the guest's values.
164168
* Zeroing XORs are dirt cheap, i.e. the extra paranoia is essentially
165169
* free. RSP and RAX are exempt as RSP is restored by hardware during
166-
* VM-Exit and RAX is explicitly loaded with 0 or 1 to return VM-Fail.
170+
* VM-Exit and RBX is explicitly loaded with 0 or 1 to hold the return
171+
* value.
167172
*/
173+
xor %eax, %eax
168174
xor %ecx, %ecx
169175
xor %edx, %edx
170-
xor %ebx, %ebx
171176
xor %ebp, %ebp
172177
xor %esi, %esi
173178
xor %edi, %edi
@@ -185,6 +190,28 @@ SYM_INNER_LABEL(vmx_vmexit, SYM_L_GLOBAL)
185190
/* "POP" @regs. */
186191
add $WORD_SIZE, %_ASM_SP
187192

193+
/*
194+
* IMPORTANT: RSB filling and SPEC_CTRL handling must be done before
195+
* the first unbalanced RET after vmexit!
196+
*
197+
* For retpoline, RSB filling is needed to prevent poisoned RSB entries
198+
* and (in some cases) RSB underflow.
199+
*
200+
* eIBRS has its own protection against poisoned RSB, so it doesn't
201+
* need the RSB filling sequence. But it does need to be enabled
202+
* before the first unbalanced RET.
203+
*/
204+
205+
FILL_RETURN_BUFFER %_ASM_CX, RSB_CLEAR_LOOPS, X86_FEATURE_RETPOLINE
206+
207+
pop %_ASM_ARG2 /* @flags */
208+
pop %_ASM_ARG1 /* @vmx */
209+
210+
call vmx_spec_ctrl_restore_host
211+
212+
/* Put return value in AX */
213+
mov %_ASM_BX, %_ASM_AX
214+
188215
pop %_ASM_BX
189216
#ifdef CONFIG_X86_64
190217
pop %r12
@@ -204,7 +231,7 @@ SYM_INNER_LABEL(vmx_vmexit, SYM_L_GLOBAL)
204231
ud2
205232
.Lvmfail:
206233
/* VM-Fail: set return value to 1 */
207-
mov $1, %eax
234+
mov $1, %_ASM_BX
208235
jmp .Lclear_regs
209236

210237
SYM_FUNC_END(__vmx_vcpu_run)

arch/x86/kvm/vmx/vmx.c

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -846,6 +846,14 @@ unsigned int __vmx_vcpu_run_flags(struct vcpu_vmx *vmx)
846846
if (vmx->loaded_vmcs->launched)
847847
flags |= VMX_RUN_VMRESUME;
848848

849+
/*
850+
* If writes to the SPEC_CTRL MSR aren't intercepted, the guest is free
851+
* to change it directly without causing a vmexit. In that case read
852+
* it after vmexit and store it in vmx->spec_ctrl.
853+
*/
854+
if (unlikely(!msr_write_intercepted(vmx, MSR_IA32_SPEC_CTRL)))
855+
flags |= VMX_RUN_SAVE_SPEC_CTRL;
856+
849857
return flags;
850858
}
851859

@@ -6823,6 +6831,26 @@ void noinstr vmx_update_host_rsp(struct vcpu_vmx *vmx, unsigned long host_rsp)
68236831
}
68246832
}
68256833

6834+
void noinstr vmx_spec_ctrl_restore_host(struct vcpu_vmx *vmx,
6835+
unsigned int flags)
6836+
{
6837+
u64 hostval = this_cpu_read(x86_spec_ctrl_current);
6838+
6839+
if (!cpu_feature_enabled(X86_FEATURE_MSR_SPEC_CTRL))
6840+
return;
6841+
6842+
if (flags & VMX_RUN_SAVE_SPEC_CTRL)
6843+
vmx->spec_ctrl = __rdmsr(MSR_IA32_SPEC_CTRL);
6844+
6845+
/*
6846+
* If the guest/host SPEC_CTRL values differ, restore the host value.
6847+
*/
6848+
if (vmx->spec_ctrl != hostval)
6849+
native_wrmsrl(MSR_IA32_SPEC_CTRL, hostval);
6850+
6851+
barrier_nospec();
6852+
}
6853+
68266854
static fastpath_t vmx_exit_handlers_fastpath(struct kvm_vcpu *vcpu)
68276855
{
68286856
switch (to_vmx(vcpu)->exit_reason.basic) {
@@ -6966,26 +6994,6 @@ static fastpath_t vmx_vcpu_run(struct kvm_vcpu *vcpu)
69666994
/* The actual VMENTER/EXIT is in the .noinstr.text section. */
69676995
vmx_vcpu_enter_exit(vcpu, vmx, __vmx_vcpu_run_flags(vmx));
69686996

6969-
/*
6970-
* We do not use IBRS in the kernel. If this vCPU has used the
6971-
* SPEC_CTRL MSR it may have left it on; save the value and
6972-
* turn it off. This is much more efficient than blindly adding
6973-
* it to the atomic save/restore list. Especially as the former
6974-
* (Saving guest MSRs on vmexit) doesn't even exist in KVM.
6975-
*
6976-
* For non-nested case:
6977-
* If the L01 MSR bitmap does not intercept the MSR, then we need to
6978-
* save it.
6979-
*
6980-
* For nested case:
6981-
* If the L02 MSR bitmap does not intercept the MSR, then we need to
6982-
* save it.
6983-
*/
6984-
if (unlikely(!msr_write_intercepted(vmx, MSR_IA32_SPEC_CTRL)))
6985-
vmx->spec_ctrl = native_read_msr(MSR_IA32_SPEC_CTRL);
6986-
6987-
x86_spec_ctrl_restore_host(vmx->spec_ctrl, 0);
6988-
69896997
/* All fields are clean at this point */
69906998
if (static_branch_unlikely(&enable_evmcs)) {
69916999
current_evmcs->hv_clean_fields |=

arch/x86/kvm/vmx/vmx.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,7 @@ void vmx_set_virtual_apic_mode(struct kvm_vcpu *vcpu);
405405
struct vmx_uret_msr *vmx_find_uret_msr(struct vcpu_vmx *vmx, u32 msr);
406406
void pt_update_intercept_for_msr(struct kvm_vcpu *vcpu);
407407
void vmx_update_host_rsp(struct vcpu_vmx *vmx, unsigned long host_rsp);
408+
void vmx_spec_ctrl_restore_host(struct vcpu_vmx *vmx, unsigned int flags);
408409
unsigned int __vmx_vcpu_run_flags(struct vcpu_vmx *vmx);
409410
bool __vmx_vcpu_run(struct vcpu_vmx *vmx, unsigned long *regs,
410411
unsigned int flags);

0 commit comments

Comments
 (0)