x86/entry: Add kernel IBRS implementation
Implement Kernel IBRS - currently the only known option to mitigate RSB underflow speculation issues on Skylake hardware. Note: since IBRS_ENTER requires fuller context established than UNTRAIN_RET, it must be placed after it. However, since UNTRAIN_RET itself implies a RET, it must come after IBRS_ENTER. This means IBRS_ENTER needs to also move UNTRAIN_RET. Note 2: KERNEL_IBRS is sub-optimal for XenPV. Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Signed-off-by: Borislav Petkov <bp@suse.de> Reviewed-by: Josh Poimboeuf <jpoimboe@kernel.org> Signed-off-by: Borislav Petkov <bp@suse.de>
This commit is contained in:
parent
caa0ff24d5
commit
2dbb887e87
|
@ -7,6 +7,8 @@
|
|||
#include <asm/asm-offsets.h>
|
||||
#include <asm/processor-flags.h>
|
||||
#include <asm/ptrace-abi.h>
|
||||
#include <asm/msr.h>
|
||||
#include <asm/nospec-branch.h>
|
||||
|
||||
/*
|
||||
|
||||
|
@ -282,6 +284,62 @@ For 32-bit we have the following conventions - kernel is built with
|
|||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* IBRS kernel mitigation for Spectre_v2.
|
||||
*
|
||||
* Assumes full context is established (PUSH_REGS, CR3 and GS) and it clobbers
|
||||
* the regs it uses (AX, CX, DX). Must be called before the first RET
|
||||
* instruction (NOTE! UNTRAIN_RET includes a RET instruction)
|
||||
*
|
||||
* The optional argument is used to save/restore the current value,
|
||||
* which is used on the paranoid paths.
|
||||
*
|
||||
* Assumes x86_spec_ctrl_{base,current} to have SPEC_CTRL_IBRS set.
|
||||
*/
|
||||
.macro IBRS_ENTER save_reg
|
||||
ALTERNATIVE "jmp .Lend_\@", "", X86_FEATURE_KERNEL_IBRS
|
||||
movl $MSR_IA32_SPEC_CTRL, %ecx
|
||||
|
||||
.ifnb \save_reg
|
||||
rdmsr
|
||||
shl $32, %rdx
|
||||
or %rdx, %rax
|
||||
mov %rax, \save_reg
|
||||
test $SPEC_CTRL_IBRS, %eax
|
||||
jz .Ldo_wrmsr_\@
|
||||
lfence
|
||||
jmp .Lend_\@
|
||||
.Ldo_wrmsr_\@:
|
||||
.endif
|
||||
|
||||
movq PER_CPU_VAR(x86_spec_ctrl_current), %rdx
|
||||
movl %edx, %eax
|
||||
shr $32, %rdx
|
||||
wrmsr
|
||||
.Lend_\@:
|
||||
.endm
|
||||
|
||||
/*
|
||||
* Similar to IBRS_ENTER, requires KERNEL GS,CR3 and clobbers (AX, CX, DX)
|
||||
* regs. Must be called after the last RET.
|
||||
*/
|
||||
.macro IBRS_EXIT save_reg
|
||||
ALTERNATIVE "jmp .Lend_\@", "", X86_FEATURE_KERNEL_IBRS
|
||||
movl $MSR_IA32_SPEC_CTRL, %ecx
|
||||
|
||||
.ifnb \save_reg
|
||||
mov \save_reg, %rdx
|
||||
.else
|
||||
movq PER_CPU_VAR(x86_spec_ctrl_current), %rdx
|
||||
andl $(~SPEC_CTRL_IBRS), %edx
|
||||
.endif
|
||||
|
||||
movl %edx, %eax
|
||||
shr $32, %rdx
|
||||
wrmsr
|
||||
.Lend_\@:
|
||||
.endm
|
||||
|
||||
/*
|
||||
* Mitigate Spectre v1 for conditional swapgs code paths.
|
||||
*
|
||||
|
|
|
@ -96,7 +96,6 @@ SYM_CODE_START(entry_SYSCALL_64)
|
|||
|
||||
SYM_INNER_LABEL(entry_SYSCALL_64_safe_stack, SYM_L_GLOBAL)
|
||||
ANNOTATE_NOENDBR
|
||||
UNTRAIN_RET
|
||||
|
||||
/* Construct struct pt_regs on stack */
|
||||
pushq $__USER_DS /* pt_regs->ss */
|
||||
|
@ -113,6 +112,11 @@ SYM_INNER_LABEL(entry_SYSCALL_64_after_hwframe, SYM_L_GLOBAL)
|
|||
movq %rsp, %rdi
|
||||
/* Sign extend the lower 32bit as syscall numbers are treated as int */
|
||||
movslq %eax, %rsi
|
||||
|
||||
/* clobbers %rax, make sure it is after saving the syscall nr */
|
||||
IBRS_ENTER
|
||||
UNTRAIN_RET
|
||||
|
||||
call do_syscall_64 /* returns with IRQs disabled */
|
||||
|
||||
/*
|
||||
|
@ -192,6 +196,7 @@ SYM_INNER_LABEL(entry_SYSCALL_64_after_hwframe, SYM_L_GLOBAL)
|
|||
* perf profiles. Nothing jumps here.
|
||||
*/
|
||||
syscall_return_via_sysret:
|
||||
IBRS_EXIT
|
||||
POP_REGS pop_rdi=0
|
||||
|
||||
/*
|
||||
|
@ -605,6 +610,7 @@ __irqentry_text_end:
|
|||
|
||||
SYM_CODE_START_LOCAL(common_interrupt_return)
|
||||
SYM_INNER_LABEL(swapgs_restore_regs_and_return_to_usermode, SYM_L_GLOBAL)
|
||||
IBRS_EXIT
|
||||
#ifdef CONFIG_DEBUG_ENTRY
|
||||
/* Assert that pt_regs indicates user mode. */
|
||||
testb $3, CS(%rsp)
|
||||
|
@ -891,6 +897,9 @@ SYM_CODE_END(xen_failsafe_callback)
|
|||
* 1 -> no SWAPGS on exit
|
||||
*
|
||||
* Y GSBASE value at entry, must be restored in paranoid_exit
|
||||
*
|
||||
* R14 - old CR3
|
||||
* R15 - old SPEC_CTRL
|
||||
*/
|
||||
SYM_CODE_START_LOCAL(paranoid_entry)
|
||||
UNWIND_HINT_FUNC
|
||||
|
@ -913,7 +922,6 @@ SYM_CODE_START_LOCAL(paranoid_entry)
|
|||
* be retrieved from a kernel internal table.
|
||||
*/
|
||||
SAVE_AND_SWITCH_TO_KERNEL_CR3 scratch_reg=%rax save_reg=%r14
|
||||
UNTRAIN_RET
|
||||
|
||||
/*
|
||||
* Handling GSBASE depends on the availability of FSGSBASE.
|
||||
|
@ -935,7 +943,7 @@ SYM_CODE_START_LOCAL(paranoid_entry)
|
|||
* is needed here.
|
||||
*/
|
||||
SAVE_AND_SET_GSBASE scratch_reg=%rax save_reg=%rbx
|
||||
RET
|
||||
jmp .Lparanoid_gsbase_done
|
||||
|
||||
.Lparanoid_entry_checkgs:
|
||||
/* EBX = 1 -> kernel GSBASE active, no restore required */
|
||||
|
@ -954,8 +962,16 @@ SYM_CODE_START_LOCAL(paranoid_entry)
|
|||
xorl %ebx, %ebx
|
||||
swapgs
|
||||
.Lparanoid_kernel_gsbase:
|
||||
|
||||
FENCE_SWAPGS_KERNEL_ENTRY
|
||||
.Lparanoid_gsbase_done:
|
||||
|
||||
/*
|
||||
* Once we have CR3 and %GS setup save and set SPEC_CTRL. Just like
|
||||
* CR3 above, keep the old value in a callee saved register.
|
||||
*/
|
||||
IBRS_ENTER save_reg=%r15
|
||||
UNTRAIN_RET
|
||||
|
||||
RET
|
||||
SYM_CODE_END(paranoid_entry)
|
||||
|
||||
|
@ -977,9 +993,19 @@ SYM_CODE_END(paranoid_entry)
|
|||
* 1 -> no SWAPGS on exit
|
||||
*
|
||||
* Y User space GSBASE, must be restored unconditionally
|
||||
*
|
||||
* R14 - old CR3
|
||||
* R15 - old SPEC_CTRL
|
||||
*/
|
||||
SYM_CODE_START_LOCAL(paranoid_exit)
|
||||
UNWIND_HINT_REGS
|
||||
|
||||
/*
|
||||
* Must restore IBRS state before both CR3 and %GS since we need access
|
||||
* to the per-CPU x86_spec_ctrl_shadow variable.
|
||||
*/
|
||||
IBRS_EXIT save_reg=%r15
|
||||
|
||||
/*
|
||||
* The order of operations is important. RESTORE_CR3 requires
|
||||
* kernel GSBASE.
|
||||
|
@ -1023,10 +1049,12 @@ SYM_CODE_START_LOCAL(error_entry)
|
|||
FENCE_SWAPGS_USER_ENTRY
|
||||
/* We have user CR3. Change to kernel CR3. */
|
||||
SWITCH_TO_KERNEL_CR3 scratch_reg=%rax
|
||||
IBRS_ENTER
|
||||
UNTRAIN_RET
|
||||
|
||||
leaq 8(%rsp), %rdi /* arg0 = pt_regs pointer */
|
||||
.Lerror_entry_from_usermode_after_swapgs:
|
||||
|
||||
/* Put us onto the real thread stack. */
|
||||
call sync_regs
|
||||
RET
|
||||
|
@ -1076,6 +1104,7 @@ SYM_CODE_START_LOCAL(error_entry)
|
|||
swapgs
|
||||
FENCE_SWAPGS_USER_ENTRY
|
||||
SWITCH_TO_KERNEL_CR3 scratch_reg=%rax
|
||||
IBRS_ENTER
|
||||
UNTRAIN_RET
|
||||
|
||||
/*
|
||||
|
@ -1172,7 +1201,6 @@ SYM_CODE_START(asm_exc_nmi)
|
|||
movq %rsp, %rdx
|
||||
movq PER_CPU_VAR(cpu_current_top_of_stack), %rsp
|
||||
UNWIND_HINT_IRET_REGS base=%rdx offset=8
|
||||
UNTRAIN_RET
|
||||
pushq 5*8(%rdx) /* pt_regs->ss */
|
||||
pushq 4*8(%rdx) /* pt_regs->rsp */
|
||||
pushq 3*8(%rdx) /* pt_regs->flags */
|
||||
|
@ -1183,6 +1211,9 @@ SYM_CODE_START(asm_exc_nmi)
|
|||
PUSH_AND_CLEAR_REGS rdx=(%rdx)
|
||||
ENCODE_FRAME_POINTER
|
||||
|
||||
IBRS_ENTER
|
||||
UNTRAIN_RET
|
||||
|
||||
/*
|
||||
* At this point we no longer need to worry about stack damage
|
||||
* due to nesting -- we're on the normal thread stack and we're
|
||||
|
@ -1407,6 +1438,9 @@ end_repeat_nmi:
|
|||
movq $-1, %rsi
|
||||
call exc_nmi
|
||||
|
||||
/* Always restore stashed SPEC_CTRL value (see paranoid_entry) */
|
||||
IBRS_EXIT save_reg=%r15
|
||||
|
||||
/* Always restore stashed CR3 value (see paranoid_entry) */
|
||||
RESTORE_CR3 scratch_reg=%r15 save_reg=%r14
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
*
|
||||
* Copyright 2000-2002 Andi Kleen, SuSE Labs.
|
||||
*/
|
||||
#include "calling.h"
|
||||
#include <asm/asm-offsets.h>
|
||||
#include <asm/current.h>
|
||||
#include <asm/errno.h>
|
||||
|
@ -18,6 +17,8 @@
|
|||
#include <linux/linkage.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
#include "calling.h"
|
||||
|
||||
.section .entry.text, "ax"
|
||||
|
||||
/*
|
||||
|
@ -73,7 +74,6 @@ SYM_CODE_START(entry_SYSENTER_compat)
|
|||
pushq $__USER32_CS /* pt_regs->cs */
|
||||
pushq $0 /* pt_regs->ip = 0 (placeholder) */
|
||||
SYM_INNER_LABEL(entry_SYSENTER_compat_after_hwframe, SYM_L_GLOBAL)
|
||||
UNTRAIN_RET
|
||||
|
||||
/*
|
||||
* User tracing code (ptrace or signal handlers) might assume that
|
||||
|
@ -90,6 +90,9 @@ SYM_INNER_LABEL(entry_SYSENTER_compat_after_hwframe, SYM_L_GLOBAL)
|
|||
|
||||
cld
|
||||
|
||||
IBRS_ENTER
|
||||
UNTRAIN_RET
|
||||
|
||||
/*
|
||||
* SYSENTER doesn't filter flags, so we need to clear NT and AC
|
||||
* ourselves. To save a few cycles, we can check whether
|
||||
|
@ -192,7 +195,6 @@ SYM_CODE_START(entry_SYSCALL_compat)
|
|||
|
||||
SYM_INNER_LABEL(entry_SYSCALL_compat_safe_stack, SYM_L_GLOBAL)
|
||||
ANNOTATE_NOENDBR
|
||||
UNTRAIN_RET
|
||||
|
||||
/* Construct struct pt_regs on stack */
|
||||
pushq $__USER32_DS /* pt_regs->ss */
|
||||
|
@ -206,6 +208,9 @@ SYM_INNER_LABEL(entry_SYSCALL_compat_after_hwframe, SYM_L_GLOBAL)
|
|||
PUSH_AND_CLEAR_REGS rcx=%rbp rax=$-ENOSYS
|
||||
UNWIND_HINT_REGS
|
||||
|
||||
IBRS_ENTER
|
||||
UNTRAIN_RET
|
||||
|
||||
movq %rsp, %rdi
|
||||
call do_fast_syscall_32
|
||||
/* XEN PV guests always use IRET path */
|
||||
|
@ -220,6 +225,8 @@ sysret32_from_system_call:
|
|||
*/
|
||||
STACKLEAK_ERASE
|
||||
|
||||
IBRS_EXIT
|
||||
|
||||
movq RBX(%rsp), %rbx /* pt_regs->rbx */
|
||||
movq RBP(%rsp), %rbp /* pt_regs->rbp */
|
||||
movq EFLAGS(%rsp), %r11 /* pt_regs->flags (in r11) */
|
||||
|
@ -335,12 +342,14 @@ SYM_CODE_START(entry_INT80_compat)
|
|||
pushq 0*8(%rax) /* regs->orig_ax */
|
||||
.Lint80_keep_stack:
|
||||
|
||||
UNTRAIN_RET
|
||||
PUSH_AND_CLEAR_REGS rax=$-ENOSYS
|
||||
UNWIND_HINT_REGS
|
||||
|
||||
cld
|
||||
|
||||
IBRS_ENTER
|
||||
UNTRAIN_RET
|
||||
|
||||
movq %rsp, %rdi
|
||||
call do_int80_syscall_32
|
||||
jmp swapgs_restore_regs_and_return_to_usermode
|
||||
|
|
|
@ -203,7 +203,7 @@
|
|||
#define X86_FEATURE_PROC_FEEDBACK ( 7*32+ 9) /* AMD ProcFeedbackInterface */
|
||||
#define X86_FEATURE_XCOMPACTED ( 7*32+10) /* "" Use compacted XSTATE (XSAVES or XSAVEC) */
|
||||
#define X86_FEATURE_PTI ( 7*32+11) /* Kernel Page Table Isolation enabled */
|
||||
/* FREE! ( 7*32+12) */
|
||||
#define X86_FEATURE_KERNEL_IBRS ( 7*32+12) /* "" Set/clear IBRS on kernel entry/exit */
|
||||
/* FREE! ( 7*32+13) */
|
||||
#define X86_FEATURE_INTEL_PPIN ( 7*32+14) /* Intel Processor Inventory Number */
|
||||
#define X86_FEATURE_CDP_L2 ( 7*32+15) /* Code and Data Prioritization L2 */
|
||||
|
|
Loading…
Reference in New Issue