Commit 2dbb887e authored by Peter Zijlstra's avatar Peter Zijlstra Committed by Borislav Petkov
Browse files

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: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: default avatarBorislav Petkov <bp@suse.de>
Reviewed-by: default avatarJosh Poimboeuf <jpoimboe@kernel.org>
Signed-off-by: default avatarBorislav Petkov <bp@suse.de>
parent caa0ff24
Loading
Loading
Loading
Loading
+58 −0
Original line number Original line Diff line number Diff line
@@ -7,6 +7,8 @@
#include <asm/asm-offsets.h>
#include <asm/asm-offsets.h>
#include <asm/processor-flags.h>
#include <asm/processor-flags.h>
#include <asm/ptrace-abi.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
#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.
 * Mitigate Spectre v1 for conditional swapgs code paths.
 *
 *
+39 −5
Original line number Original line Diff line number Diff line
@@ -96,7 +96,6 @@ SYM_CODE_START(entry_SYSCALL_64)


SYM_INNER_LABEL(entry_SYSCALL_64_safe_stack, SYM_L_GLOBAL)
SYM_INNER_LABEL(entry_SYSCALL_64_safe_stack, SYM_L_GLOBAL)
	ANNOTATE_NOENDBR
	ANNOTATE_NOENDBR
	UNTRAIN_RET


	/* Construct struct pt_regs on stack */
	/* Construct struct pt_regs on stack */
	pushq	$__USER_DS				/* pt_regs->ss */
	pushq	$__USER_DS				/* pt_regs->ss */
@@ -113,6 +112,11 @@ SYM_INNER_LABEL(entry_SYSCALL_64_after_hwframe, SYM_L_GLOBAL)
	movq	%rsp, %rdi
	movq	%rsp, %rdi
	/* Sign extend the lower 32bit as syscall numbers are treated as int */
	/* Sign extend the lower 32bit as syscall numbers are treated as int */
	movslq	%eax, %rsi
	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 */
	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.
	 * perf profiles. Nothing jumps here.
	 */
	 */
syscall_return_via_sysret:
syscall_return_via_sysret:
	IBRS_EXIT
	POP_REGS pop_rdi=0
	POP_REGS pop_rdi=0


	/*
	/*
@@ -605,6 +610,7 @@ __irqentry_text_end:


SYM_CODE_START_LOCAL(common_interrupt_return)
SYM_CODE_START_LOCAL(common_interrupt_return)
SYM_INNER_LABEL(swapgs_restore_regs_and_return_to_usermode, SYM_L_GLOBAL)
SYM_INNER_LABEL(swapgs_restore_regs_and_return_to_usermode, SYM_L_GLOBAL)
	IBRS_EXIT
#ifdef CONFIG_DEBUG_ENTRY
#ifdef CONFIG_DEBUG_ENTRY
	/* Assert that pt_regs indicates user mode. */
	/* Assert that pt_regs indicates user mode. */
	testb	$3, CS(%rsp)
	testb	$3, CS(%rsp)
@@ -891,6 +897,9 @@ SYM_CODE_END(xen_failsafe_callback)
 *              1 -> no SWAPGS on exit
 *              1 -> no SWAPGS on exit
 *
 *
 *     Y        GSBASE value at entry, must be restored in paranoid_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)
SYM_CODE_START_LOCAL(paranoid_entry)
	UNWIND_HINT_FUNC
	UNWIND_HINT_FUNC
@@ -913,7 +922,6 @@ SYM_CODE_START_LOCAL(paranoid_entry)
	 * be retrieved from a kernel internal table.
	 * be retrieved from a kernel internal table.
	 */
	 */
	SAVE_AND_SWITCH_TO_KERNEL_CR3 scratch_reg=%rax save_reg=%r14
	SAVE_AND_SWITCH_TO_KERNEL_CR3 scratch_reg=%rax save_reg=%r14
	UNTRAIN_RET


	/*
	/*
	 * Handling GSBASE depends on the availability of FSGSBASE.
	 * Handling GSBASE depends on the availability of FSGSBASE.
@@ -935,7 +943,7 @@ SYM_CODE_START_LOCAL(paranoid_entry)
	 * is needed here.
	 * is needed here.
	 */
	 */
	SAVE_AND_SET_GSBASE scratch_reg=%rax save_reg=%rbx
	SAVE_AND_SET_GSBASE scratch_reg=%rax save_reg=%rbx
	RET
	jmp .Lparanoid_gsbase_done


.Lparanoid_entry_checkgs:
.Lparanoid_entry_checkgs:
	/* EBX = 1 -> kernel GSBASE active, no restore required */
	/* EBX = 1 -> kernel GSBASE active, no restore required */
@@ -954,8 +962,16 @@ SYM_CODE_START_LOCAL(paranoid_entry)
	xorl	%ebx, %ebx
	xorl	%ebx, %ebx
	swapgs
	swapgs
.Lparanoid_kernel_gsbase:
.Lparanoid_kernel_gsbase:

	FENCE_SWAPGS_KERNEL_ENTRY
	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
	RET
SYM_CODE_END(paranoid_entry)
SYM_CODE_END(paranoid_entry)


@@ -977,9 +993,19 @@ SYM_CODE_END(paranoid_entry)
 *              1 -> no SWAPGS on exit
 *              1 -> no SWAPGS on exit
 *
 *
 *     Y        User space GSBASE, must be restored unconditionally
 *     Y        User space GSBASE, must be restored unconditionally
 *
 * R14 - old CR3
 * R15 - old SPEC_CTRL
 */
 */
SYM_CODE_START_LOCAL(paranoid_exit)
SYM_CODE_START_LOCAL(paranoid_exit)
	UNWIND_HINT_REGS
	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
	 * The order of operations is important. RESTORE_CR3 requires
	 * kernel GSBASE.
	 * kernel GSBASE.
@@ -1023,10 +1049,12 @@ SYM_CODE_START_LOCAL(error_entry)
	FENCE_SWAPGS_USER_ENTRY
	FENCE_SWAPGS_USER_ENTRY
	/* We have user CR3.  Change to kernel CR3. */
	/* We have user CR3.  Change to kernel CR3. */
	SWITCH_TO_KERNEL_CR3 scratch_reg=%rax
	SWITCH_TO_KERNEL_CR3 scratch_reg=%rax
	IBRS_ENTER
	UNTRAIN_RET
	UNTRAIN_RET


	leaq	8(%rsp), %rdi			/* arg0 = pt_regs pointer */
	leaq	8(%rsp), %rdi			/* arg0 = pt_regs pointer */
.Lerror_entry_from_usermode_after_swapgs:
.Lerror_entry_from_usermode_after_swapgs:

	/* Put us onto the real thread stack. */
	/* Put us onto the real thread stack. */
	call	sync_regs
	call	sync_regs
	RET
	RET
@@ -1076,6 +1104,7 @@ SYM_CODE_START_LOCAL(error_entry)
	swapgs
	swapgs
	FENCE_SWAPGS_USER_ENTRY
	FENCE_SWAPGS_USER_ENTRY
	SWITCH_TO_KERNEL_CR3 scratch_reg=%rax
	SWITCH_TO_KERNEL_CR3 scratch_reg=%rax
	IBRS_ENTER
	UNTRAIN_RET
	UNTRAIN_RET


	/*
	/*
@@ -1172,7 +1201,6 @@ SYM_CODE_START(asm_exc_nmi)
	movq	%rsp, %rdx
	movq	%rsp, %rdx
	movq	PER_CPU_VAR(cpu_current_top_of_stack), %rsp
	movq	PER_CPU_VAR(cpu_current_top_of_stack), %rsp
	UNWIND_HINT_IRET_REGS base=%rdx offset=8
	UNWIND_HINT_IRET_REGS base=%rdx offset=8
	UNTRAIN_RET
	pushq	5*8(%rdx)	/* pt_regs->ss */
	pushq	5*8(%rdx)	/* pt_regs->ss */
	pushq	4*8(%rdx)	/* pt_regs->rsp */
	pushq	4*8(%rdx)	/* pt_regs->rsp */
	pushq	3*8(%rdx)	/* pt_regs->flags */
	pushq	3*8(%rdx)	/* pt_regs->flags */
@@ -1183,6 +1211,9 @@ SYM_CODE_START(asm_exc_nmi)
	PUSH_AND_CLEAR_REGS rdx=(%rdx)
	PUSH_AND_CLEAR_REGS rdx=(%rdx)
	ENCODE_FRAME_POINTER
	ENCODE_FRAME_POINTER


	IBRS_ENTER
	UNTRAIN_RET

	/*
	/*
	 * At this point we no longer need to worry about stack damage
	 * 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
	 * due to nesting -- we're on the normal thread stack and we're
@@ -1407,6 +1438,9 @@ end_repeat_nmi:
	movq	$-1, %rsi
	movq	$-1, %rsi
	call	exc_nmi
	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) */
	/* Always restore stashed CR3 value (see paranoid_entry) */
	RESTORE_CR3 scratch_reg=%r15 save_reg=%r14
	RESTORE_CR3 scratch_reg=%r15 save_reg=%r14


+13 −4
Original line number Original line Diff line number Diff line
@@ -4,7 +4,6 @@
 *
 *
 * Copyright 2000-2002 Andi Kleen, SuSE Labs.
 * Copyright 2000-2002 Andi Kleen, SuSE Labs.
 */
 */
#include "calling.h"
#include <asm/asm-offsets.h>
#include <asm/asm-offsets.h>
#include <asm/current.h>
#include <asm/current.h>
#include <asm/errno.h>
#include <asm/errno.h>
@@ -18,6 +17,8 @@
#include <linux/linkage.h>
#include <linux/linkage.h>
#include <linux/err.h>
#include <linux/err.h>


#include "calling.h"

	.section .entry.text, "ax"
	.section .entry.text, "ax"


/*
/*
@@ -73,7 +74,6 @@ SYM_CODE_START(entry_SYSENTER_compat)
	pushq	$__USER32_CS		/* pt_regs->cs */
	pushq	$__USER32_CS		/* pt_regs->cs */
	pushq	$0			/* pt_regs->ip = 0 (placeholder) */
	pushq	$0			/* pt_regs->ip = 0 (placeholder) */
SYM_INNER_LABEL(entry_SYSENTER_compat_after_hwframe, SYM_L_GLOBAL)
SYM_INNER_LABEL(entry_SYSENTER_compat_after_hwframe, SYM_L_GLOBAL)
	UNTRAIN_RET


	/*
	/*
	 * User tracing code (ptrace or signal handlers) might assume that
	 * 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
	cld


	IBRS_ENTER
	UNTRAIN_RET

	/*
	/*
	 * SYSENTER doesn't filter flags, so we need to clear NT and AC
	 * SYSENTER doesn't filter flags, so we need to clear NT and AC
	 * ourselves.  To save a few cycles, we can check whether
	 * 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)
SYM_INNER_LABEL(entry_SYSCALL_compat_safe_stack, SYM_L_GLOBAL)
	ANNOTATE_NOENDBR
	ANNOTATE_NOENDBR
	UNTRAIN_RET


	/* Construct struct pt_regs on stack */
	/* Construct struct pt_regs on stack */
	pushq	$__USER32_DS		/* pt_regs->ss */
	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
	PUSH_AND_CLEAR_REGS rcx=%rbp rax=$-ENOSYS
	UNWIND_HINT_REGS
	UNWIND_HINT_REGS


	IBRS_ENTER
	UNTRAIN_RET

	movq	%rsp, %rdi
	movq	%rsp, %rdi
	call	do_fast_syscall_32
	call	do_fast_syscall_32
	/* XEN PV guests always use IRET path */
	/* XEN PV guests always use IRET path */
@@ -220,6 +225,8 @@ sysret32_from_system_call:
	 */
	 */
	STACKLEAK_ERASE
	STACKLEAK_ERASE


	IBRS_EXIT

	movq	RBX(%rsp), %rbx		/* pt_regs->rbx */
	movq	RBX(%rsp), %rbx		/* pt_regs->rbx */
	movq	RBP(%rsp), %rbp		/* pt_regs->rbp */
	movq	RBP(%rsp), %rbp		/* pt_regs->rbp */
	movq	EFLAGS(%rsp), %r11	/* pt_regs->flags (in r11) */
	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 */
	pushq	0*8(%rax)		/* regs->orig_ax */
.Lint80_keep_stack:
.Lint80_keep_stack:


	UNTRAIN_RET
	PUSH_AND_CLEAR_REGS rax=$-ENOSYS
	PUSH_AND_CLEAR_REGS rax=$-ENOSYS
	UNWIND_HINT_REGS
	UNWIND_HINT_REGS


	cld
	cld


	IBRS_ENTER
	UNTRAIN_RET

	movq	%rsp, %rdi
	movq	%rsp, %rdi
	call	do_int80_syscall_32
	call	do_int80_syscall_32
	jmp	swapgs_restore_regs_and_return_to_usermode
	jmp	swapgs_restore_regs_and_return_to_usermode
+1 −1
Original line number Original line Diff line number Diff line
@@ -203,7 +203,7 @@
#define X86_FEATURE_PROC_FEEDBACK	( 7*32+ 9) /* AMD ProcFeedbackInterface */
#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_XCOMPACTED		( 7*32+10) /* "" Use compacted XSTATE (XSAVES or XSAVEC) */
#define X86_FEATURE_PTI			( 7*32+11) /* Kernel Page Table Isolation enabled */
#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) */
/* FREE!				( 7*32+13) */
#define X86_FEATURE_INTEL_PPIN		( 7*32+14) /* Intel Processor Inventory Number */
#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 */
#define X86_FEATURE_CDP_L2		( 7*32+15) /* Code and Data Prioritization L2 */