Re: [PATCH 6/6] s390: introduce execute-trampolines for branches

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed 2018-02-07 08:00:11, Martin Schwidefsky wrote:
> Add CONFIG_EXPOLINE to enable the use of the new -mindirect-branch= and
> -mfunction_return= compiler options to create a kernel fortified against
> the specte v2 attack.
> 
> With CONFIG_EXPOLINE=y all indirect branches will be issued with an
> execute type instruction. For z10 or newer the EXRL instruction will
> be used, for older machines the EX instruction. The typical indirect
> call
> 
> 	basr	%r14,%r1
> 
> is replaced with a PC relative call to a new thunk
> 
> 	brasl	%r14,__s390x_indirect_jump_r1
> 
> The thunk contains the EXRL/EX instruction to the indirect branch
> 
> __s390x_indirect_jump_r1:
> 	exrl	0,0f
> 	j	.
> 0:	br	%r1
> 
> The detour via the execute type instruction has a performance impact.
> To get rid of the detour the new kernel parameter "nospectre_v2" and
> "spectre_v2=[on,off,auto]" can be used. If the parameter is specified
> the kernel and module code will be patched at runtime.

This is really unfortunate naming of kernel option.

spectre_v2=off sounds like we are turning the "bug" off, but i somehow
suspect you are turning the bug _workaround_ off.

									Pavel

> Signed-off-by: Martin Schwidefsky <schwidefsky@xxxxxxxxxx>
> ---
>  arch/s390/Kconfig                     |  28 +++++++++
>  arch/s390/Makefile                    |  12 ++++
>  arch/s390/include/asm/lowcore.h       |   6 +-
>  arch/s390/include/asm/nospec-branch.h |  18 ++++++
>  arch/s390/kernel/Makefile             |   4 ++
>  arch/s390/kernel/entry.S              | 113 ++++++++++++++++++++++++++--------
>  arch/s390/kernel/module.c             |  62 ++++++++++++++++---
>  arch/s390/kernel/nospec-branch.c      | 100 ++++++++++++++++++++++++++++++
>  arch/s390/kernel/setup.c              |   4 ++
>  arch/s390/kernel/smp.c                |   1 +
>  arch/s390/kernel/vmlinux.lds.S        |  14 +++++
>  drivers/s390/char/Makefile            |   2 +
>  12 files changed, 329 insertions(+), 35 deletions(-)
>  create mode 100644 arch/s390/include/asm/nospec-branch.h
>  create mode 100644 arch/s390/kernel/nospec-branch.c
> 
> diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
> index d514e25..d4a65bf 100644
> --- a/arch/s390/Kconfig
> +++ b/arch/s390/Kconfig
> @@ -557,6 +557,34 @@ config KERNEL_NOBP
>  
>  	  If unsure, say N.
>  
> +config EXPOLINE
> +	def_bool n
> +	prompt "Avoid speculative indirect branches in the kernel"
> +	help
> +	  Compile the kernel with the expoline compiler options to guard
> +	  against kernel-to-user data leaks by avoiding speculative indirect
> +	  branches.
> +	  Requires a compiler with -mindirect-branch=thunk support for full
> +	  protection. The kernel may run slower.
> +
> +	  If unsure, say N.
> +
> +choice
> +	prompt "Expoline default"
> +	depends on EXPOLINE
> +	default EXPOLINE_FULL
> +
> +config EXPOLINE_OFF
> +	bool "spectre_v2=off"
> +
> +config EXPOLINE_MEDIUM
> +	bool "spectre_v2=auto"
> +
> +config EXPOLINE_FULL
> +	bool "spectre_v2=on"
> +
> +endchoice
> +
>  endmenu
>  
>  menu "Memory setup"
> diff --git a/arch/s390/Makefile b/arch/s390/Makefile
> index fd691c4..2f925ef 100644
> --- a/arch/s390/Makefile
> +++ b/arch/s390/Makefile
> @@ -78,6 +78,18 @@ ifeq ($(call cc-option-yn,-mwarn-dynamicstack),y)
>  cflags-$(CONFIG_WARN_DYNAMIC_STACK) += -mwarn-dynamicstack
>  endif
>  
> +ifdef CONFIG_EXPOLINE
> +  ifeq ($(call cc-option-yn,$(CC_FLAGS_MARCH) -mindirect-branch=thunk),y)
> +    CC_FLAGS_EXPOLINE := -mindirect-branch=thunk
> +    CC_FLAGS_EXPOLINE += -mfunction-return=thunk
> +    CC_FLAGS_EXPOLINE += -mindirect-branch-table
> +    export CC_FLAGS_EXPOLINE
> +    cflags-y += $(CC_FLAGS_EXPOLINE)
> +  else
> +    $(warning "Your gcc lacks the -mindirect-branch= option")
> +  endif
> +endif
> +
>  ifdef CONFIG_FUNCTION_TRACER
>  # make use of hotpatch feature if the compiler supports it
>  cc_hotpatch	:= -mhotpatch=0,3
> diff --git a/arch/s390/include/asm/lowcore.h b/arch/s390/include/asm/lowcore.h
> index c63986a..5bc8888 100644
> --- a/arch/s390/include/asm/lowcore.h
> +++ b/arch/s390/include/asm/lowcore.h
> @@ -136,7 +136,11 @@ struct lowcore {
>  	__u64	vdso_per_cpu_data;		/* 0x03b8 */
>  	__u64	machine_flags;			/* 0x03c0 */
>  	__u64	gmap;				/* 0x03c8 */
> -	__u8	pad_0x03d0[0x0e00-0x03d0];	/* 0x03d0 */
> +	__u8	pad_0x03d0[0x0400-0x03d0];	/* 0x03d0 */
> +
> +	/* br %r1 trampoline */
> +	__u16	br_r1_trampoline;		/* 0x0400 */
> +	__u8	pad_0x0402[0x0e00-0x0402];	/* 0x0402 */
>  
>  	/*
>  	 * 0xe00 contains the address of the IPL Parameter Information
> diff --git a/arch/s390/include/asm/nospec-branch.h b/arch/s390/include/asm/nospec-branch.h
> new file mode 100644
> index 0000000..7df48e5
> --- /dev/null
> +++ b/arch/s390/include/asm/nospec-branch.h
> @@ -0,0 +1,18 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef _ASM_S390_EXPOLINE_H
> +#define _ASM_S390_EXPOLINE_H
> +
> +#ifndef __ASSEMBLY__
> +
> +#include <linux/types.h>
> +
> +extern int nospec_call_disable;
> +extern int nospec_return_disable;
> +
> +void nospec_init_branches(void);
> +void nospec_call_revert(s32 *start, s32 *end);
> +void nospec_return_revert(s32 *start, s32 *end);
> +
> +#endif /* __ASSEMBLY__ */
> +
> +#endif /* _ASM_S390_EXPOLINE_H */
> diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile
> index 909bce6..7f27e3d 100644
> --- a/arch/s390/kernel/Makefile
> +++ b/arch/s390/kernel/Makefile
> @@ -29,6 +29,7 @@ UBSAN_SANITIZE_early.o	:= n
>  #
>  ifneq ($(CC_FLAGS_MARCH),-march=z900)
>  CFLAGS_REMOVE_als.o	+= $(CC_FLAGS_MARCH)
> +CFLAGS_REMOVE_als.o	+= $(CC_FLAGS_EXPOLINE)
>  CFLAGS_als.o		+= -march=z900
>  AFLAGS_REMOVE_head.o	+= $(CC_FLAGS_MARCH)
>  AFLAGS_head.o		+= -march=z900
> @@ -63,6 +64,9 @@ obj-y	+= entry.o reipl.o relocate_kernel.o kdebugfs.o alternative.o
>  
>  extra-y				+= head.o head64.o vmlinux.lds
>  
> +obj-$(CONFIG_EXPOLINE)		+= nospec-branch.o
> +CFLAGS_REMOVE_expoline.o	+= $(CC_FLAGS_EXPOLINE)
> +
>  obj-$(CONFIG_MODULES)		+= module.o
>  obj-$(CONFIG_SMP)		+= smp.o
>  obj-$(CONFIG_SCHED_TOPOLOGY)	+= topology.o
> diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S
> index 53145b5..13a133a 100644
> --- a/arch/s390/kernel/entry.S
> +++ b/arch/s390/kernel/entry.S
> @@ -222,6 +222,68 @@ _PIF_WORK	= (_PIF_PER_TRAP | _PIF_SYSCALL_RESTART)
>  	.popsection
>  	.endm
>  
> +#ifdef CONFIG_EXPOLINE
> +
> +	.macro GEN_BR_THUNK name,reg,tmp
> +	.section .text.\name,"axG",@progbits,\name,comdat
> +	.globl \name
> +	.hidden \name
> +	.type \name,@function
> +\name:
> +	.cfi_startproc
> +#ifdef CONFIG_HAVE_MARCH_Z10_FEATURES
> +	exrl	0,0f
> +#else
> +	larl	\tmp,0f
> +	ex	0,0(\tmp)
> +#endif
> +	j	.
> +0:	br	\reg
> +	.cfi_endproc
> +	.endm
> +
> +	GEN_BR_THUNK __s390x_indirect_jump_r1use_r9,%r9,%r1
> +	GEN_BR_THUNK __s390x_indirect_jump_r1use_r14,%r14,%r1
> +	GEN_BR_THUNK __s390x_indirect_jump_r11use_r14,%r14,%r11
> +
> +	.macro BASR_R14_R9
> +0:	brasl	%r14,__s390x_indirect_jump_r1use_r9
> +	.pushsection .s390_indirect_branches,"a",@progbits
> +	.long	0b-.
> +	.popsection
> +	.endm
> +
> +	.macro BR_R1USE_R14
> +0:	jg	__s390x_indirect_jump_r1use_r14
> +	.pushsection .s390_indirect_branches,"a",@progbits
> +	.long	0b-.
> +	.popsection
> +	.endm
> +
> +	.macro BR_R11USE_R14
> +0:	jg	__s390x_indirect_jump_r11use_r14
> +	.pushsection .s390_indirect_branches,"a",@progbits
> +	.long	0b-.
> +	.popsection
> +	.endm
> +
> +#else	/* CONFIG_EXPOLINE */
> +
> +	.macro BASR_R14_R9
> +	basr	%r14,%r9
> +	.endm
> +
> +	.macro BR_R1USE_R14
> +	br	%r14
> +	.endm
> +
> +	.macro BR_R11USE_R14
> +	br	%r14
> +	.endm
> +
> +#endif /* CONFIG_EXPOLINE */
> +
> +
>  	.section .kprobes.text, "ax"
>  .Ldummy:
>  	/*
> @@ -237,7 +299,7 @@ _PIF_WORK	= (_PIF_PER_TRAP | _PIF_SYSCALL_RESTART)
>  ENTRY(__bpon)
>  	.globl __bpon
>  	BPON
> -	br	%r14
> +	BR_R1USE_R14
>  
>  /*
>   * Scheduler resume function, called by switch_to
> @@ -261,9 +323,9 @@ ENTRY(__switch_to)
>  	mvc	__LC_CURRENT_PID(4,%r0),0(%r3)	# store pid of next
>  	lmg	%r6,%r15,__SF_GPRS(%r15)	# load gprs of next task
>  	TSTMSK	__LC_MACHINE_FLAGS,MACHINE_FLAG_LPP
> -	bzr	%r14
> +	jz	0f
>  	.insn	s,0xb2800000,__LC_LPP		# set program parameter
> -	br	%r14
> +0:	BR_R1USE_R14
>  
>  .L__critical_start:
>  
> @@ -330,7 +392,7 @@ sie_exit:
>  	xgr	%r5,%r5
>  	lmg	%r6,%r14,__SF_GPRS(%r15)	# restore kernel registers
>  	lg	%r2,__SF_EMPTY+16(%r15)		# return exit reason code
> -	br	%r14
> +	BR_R1USE_R14
>  .Lsie_fault:
>  	lghi	%r14,-EFAULT
>  	stg	%r14,__SF_EMPTY+16(%r15)	# set exit reason code
> @@ -389,7 +451,7 @@ ENTRY(system_call)
>  	lgf	%r9,0(%r8,%r10)			# get system call add.
>  	TSTMSK	__TI_flags(%r12),_TIF_TRACE
>  	jnz	.Lsysc_tracesys
> -	basr	%r14,%r9			# call sys_xxxx
> +	BASR_R14_R9				# call sys_xxxx
>  	stg	%r2,__PT_R2(%r11)		# store return value
>  
>  .Lsysc_return:
> @@ -574,7 +636,7 @@ ENTRY(system_call)
>  	lmg	%r3,%r7,__PT_R3(%r11)
>  	stg	%r7,STACK_FRAME_OVERHEAD(%r15)
>  	lg	%r2,__PT_ORIG_GPR2(%r11)
> -	basr	%r14,%r9		# call sys_xxx
> +	BASR_R14_R9			# call sys_xxx
>  	stg	%r2,__PT_R2(%r11)	# store return value
>  .Lsysc_tracenogo:
>  	TSTMSK	__TI_flags(%r12),_TIF_TRACE
> @@ -598,7 +660,7 @@ ENTRY(ret_from_fork)
>  	lmg	%r9,%r10,__PT_R9(%r11)	# load gprs
>  ENTRY(kernel_thread_starter)
>  	la	%r2,0(%r10)
> -	basr	%r14,%r9
> +	BASR_R14_R9
>  	j	.Lsysc_tracenogo
>  
>  /*
> @@ -678,9 +740,9 @@ ENTRY(pgm_check_handler)
>  	nill	%r10,0x007f
>  	sll	%r10,2
>  	je	.Lpgm_return
> -	lgf	%r1,0(%r10,%r1)		# load address of handler routine
> +	lgf	%r9,0(%r10,%r1)		# load address of handler routine
>  	lgr	%r2,%r11		# pass pointer to pt_regs
> -	basr	%r14,%r1		# branch to interrupt-handler
> +	BASR_R14_R9			# branch to interrupt-handler
>  .Lpgm_return:
>  	LOCKDEP_SYS_EXIT
>  	tm	__PT_PSW+1(%r11),0x01	# returning to user ?
> @@ -998,7 +1060,7 @@ ENTRY(psw_idle)
>  	stpt	__TIMER_IDLE_ENTER(%r2)
>  .Lpsw_idle_lpsw:
>  	lpswe	__SF_EMPTY(%r15)
> -	br	%r14
> +	BR_R1USE_R14
>  .Lpsw_idle_end:
>  
>  /*
> @@ -1012,7 +1074,7 @@ ENTRY(save_fpu_regs)
>  	lg	%r2,__LC_CURRENT
>  	aghi	%r2,__TASK_thread
>  	TSTMSK	__LC_CPU_FLAGS,_CIF_FPU
> -	bor	%r14
> +	jo	.Lsave_fpu_regs_exit
>  	stfpc	__THREAD_FPU_fpc(%r2)
>  	lg	%r3,__THREAD_FPU_regs(%r2)
>  	TSTMSK	__LC_MACHINE_FLAGS,MACHINE_FLAG_VX
> @@ -1039,7 +1101,8 @@ ENTRY(save_fpu_regs)
>  	std	15,120(%r3)
>  .Lsave_fpu_regs_done:
>  	oi	__LC_CPU_FLAGS+7,_CIF_FPU
> -	br	%r14
> +.Lsave_fpu_regs_exit:
> +	BR_R1USE_R14
>  .Lsave_fpu_regs_end:
>  EXPORT_SYMBOL(save_fpu_regs)
>  
> @@ -1057,7 +1120,7 @@ load_fpu_regs:
>  	lg	%r4,__LC_CURRENT
>  	aghi	%r4,__TASK_thread
>  	TSTMSK	__LC_CPU_FLAGS,_CIF_FPU
> -	bnor	%r14
> +	jno	.Lload_fpu_regs_exit
>  	lfpc	__THREAD_FPU_fpc(%r4)
>  	TSTMSK	__LC_MACHINE_FLAGS,MACHINE_FLAG_VX
>  	lg	%r4,__THREAD_FPU_regs(%r4)	# %r4 <- reg save area
> @@ -1084,7 +1147,8 @@ load_fpu_regs:
>  	ld	15,120(%r4)
>  .Lload_fpu_regs_done:
>  	ni	__LC_CPU_FLAGS+7,255-_CIF_FPU
> -	br	%r14
> +.Lload_fpu_regs_exit:
> +	BR_R1USE_R14
>  .Lload_fpu_regs_end:
>  
>  .L__critical_end:
> @@ -1301,7 +1365,7 @@ cleanup_critical:
>  	jl	0f
>  	clg	%r9,BASED(.Lcleanup_table+104)	# .Lload_fpu_regs_end
>  	jl	.Lcleanup_load_fpu_regs
> -0:	br	%r14
> +0:	BR_R11USE_R14
>  
>  	.align	8
>  .Lcleanup_table:
> @@ -1337,7 +1401,7 @@ cleanup_critical:
>  	ni	__SIE_PROG0C+3(%r9),0xfe	# no longer in SIE
>  	lctlg	%c1,%c1,__LC_USER_ASCE		# load primary asce
>  	larl	%r9,sie_exit			# skip forward to sie_exit
> -	br	%r14
> +	BR_R11USE_R14
>  #endif
>  
>  .Lcleanup_system_call:
> @@ -1390,7 +1454,7 @@ cleanup_critical:
>  	stg	%r15,56(%r11)		# r15 stack pointer
>  	# set new psw address and exit
>  	larl	%r9,.Lsysc_do_svc
> -	br	%r14
> +	BR_R11USE_R14
>  .Lcleanup_system_call_insn:
>  	.quad	system_call
>  	.quad	.Lsysc_stmg
> @@ -1402,7 +1466,7 @@ cleanup_critical:
>  
>  .Lcleanup_sysc_tif:
>  	larl	%r9,.Lsysc_tif
> -	br	%r14
> +	BR_R11USE_R14
>  
>  .Lcleanup_sysc_restore:
>  	# check if stpt has been executed
> @@ -1419,14 +1483,14 @@ cleanup_critical:
>  	mvc	0(64,%r11),__PT_R8(%r9)
>  	lmg	%r0,%r7,__PT_R0(%r9)
>  1:	lmg	%r8,%r9,__LC_RETURN_PSW
> -	br	%r14
> +	BR_R11USE_R14
>  .Lcleanup_sysc_restore_insn:
>  	.quad	.Lsysc_exit_timer
>  	.quad	.Lsysc_done - 4
>  
>  .Lcleanup_io_tif:
>  	larl	%r9,.Lio_tif
> -	br	%r14
> +	BR_R11USE_R14
>  
>  .Lcleanup_io_restore:
>  	# check if stpt has been executed
> @@ -1440,7 +1504,7 @@ cleanup_critical:
>  	mvc	0(64,%r11),__PT_R8(%r9)
>  	lmg	%r0,%r7,__PT_R0(%r9)
>  1:	lmg	%r8,%r9,__LC_RETURN_PSW
> -	br	%r14
> +	BR_R11USE_R14
>  .Lcleanup_io_restore_insn:
>  	.quad	.Lio_exit_timer
>  	.quad	.Lio_done - 4
> @@ -1493,17 +1557,17 @@ cleanup_critical:
>  	# prepare return psw
>  	nihh	%r8,0xfcfd		# clear irq & wait state bits
>  	lg	%r9,48(%r11)		# return from psw_idle
> -	br	%r14
> +	BR_R11USE_R14
>  .Lcleanup_idle_insn:
>  	.quad	.Lpsw_idle_lpsw
>  
>  .Lcleanup_save_fpu_regs:
>  	larl	%r9,save_fpu_regs
> -	br	%r14
> +	BR_R11USE_R14
>  
>  .Lcleanup_load_fpu_regs:
>  	larl	%r9,load_fpu_regs
> -	br	%r14
> +	BR_R11USE_R14
>  
>  /*
>   * Integer constants
> @@ -1523,7 +1587,6 @@ cleanup_critical:
>  .Lsie_crit_mcck_length:
>  	.quad   .Lsie_skip - .Lsie_entry
>  #endif
> -
>  	.section .rodata, "a"
>  #define SYSCALL(esame,emu)	.long esame
>  	.globl	sys_call_table
> diff --git a/arch/s390/kernel/module.c b/arch/s390/kernel/module.c
> index b7abfad..1fc6d1f 100644
> --- a/arch/s390/kernel/module.c
> +++ b/arch/s390/kernel/module.c
> @@ -19,6 +19,8 @@
>  #include <linux/moduleloader.h>
>  #include <linux/bug.h>
>  #include <asm/alternative.h>
> +#include <asm/nospec-branch.h>
> +#include <asm/facility.h>
>  
>  #if 0
>  #define DEBUGP printk
> @@ -156,7 +158,11 @@ int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
>  	me->arch.got_offset = me->core_layout.size;
>  	me->core_layout.size += me->arch.got_size;
>  	me->arch.plt_offset = me->core_layout.size;
> -	me->core_layout.size += me->arch.plt_size;
> +	if (me->arch.plt_size) {
> +		if (IS_ENABLED(CONFIG_EXPOLINE) && !nospec_call_disable)
> +			me->arch.plt_size += PLT_ENTRY_SIZE;
> +		me->core_layout.size += me->arch.plt_size;
> +	}
>  	return 0;
>  }
>  
> @@ -310,9 +316,21 @@ static int apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
>  			unsigned int *ip;
>  			ip = me->core_layout.base + me->arch.plt_offset +
>  				info->plt_offset;
> -			ip[0] = 0x0d10e310; /* basr 1,0; lg 1,10(1); br 1 */
> -			ip[1] = 0x100a0004;
> -			ip[2] = 0x07f10000;
> +			ip[0] = 0x0d10e310;	/* basr 1,0  */
> +			ip[1] = 0x100a0004;	/* lg	1,10(1) */
> +			if (IS_ENABLED(CONFIG_EXPOLINE) &&
> +			    !nospec_call_disable) {
> +				unsigned int *ij;
> +				ij = me->core_layout.base +
> +					me->arch.plt_offset +
> +					me->arch.plt_size - PLT_ENTRY_SIZE;
> +				ip[2] = 0xa7f40000 +	/* j __jump_r1 */
> +					(unsigned int)(u16)
> +					(((unsigned long) ij - 8 -
> +					  (unsigned long) ip) / 2);
> +			} else {
> +				ip[2] = 0x07f10000;	/* br %r1 */
> +			}
>  			ip[3] = (unsigned int) (val >> 32);
>  			ip[4] = (unsigned int) val;
>  			info->plt_initialized = 1;
> @@ -418,16 +436,42 @@ int module_finalize(const Elf_Ehdr *hdr,
>  		    struct module *me)
>  {
>  	const Elf_Shdr *s;
> -	char *secstrings;
> +	char *secstrings, *secname;
> +	void *aseg;
> +
> +	if (IS_ENABLED(CONFIG_EXPOLINE) &&
> +	    !nospec_call_disable && me->arch.plt_size) {
> +		unsigned int *ij;
> +
> +		ij = me->core_layout.base + me->arch.plt_offset +
> +			me->arch.plt_size - PLT_ENTRY_SIZE;
> +		if (test_facility(35)) {
> +			ij[0] = 0xc6000000;	/* exrl	%r0,.+10	*/
> +			ij[1] = 0x0005a7f4;	/* j	.		*/
> +			ij[2] = 0x000007f1;	/* br	%r1		*/
> +		} else {
> +			ij[0] = 0x44000000 | (unsigned int)
> +				offsetof(struct lowcore, br_r1_trampoline);
> +			ij[1] = 0xa7f40000;	/* j	.		*/
> +		}
> +	}
>  
>  	secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
>  	for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++) {
> -		if (!strcmp(".altinstructions", secstrings + s->sh_name)) {
> -			/* patch .altinstructions */
> -			void *aseg = (void *)s->sh_addr;
> +		aseg = (void *) s->sh_addr;
> +		secname = secstrings + s->sh_name;
>  
> +		if (!strcmp(".altinstructions", secname))
> +			/* patch .altinstructions */
>  			apply_alternatives(aseg, aseg + s->sh_size);
> -		}
> +
> +		if (IS_ENABLED(CONFIG_EXPOLINE) &&
> +		    (!strcmp(".nospec_call_table", secname)))
> +			nospec_call_revert(aseg, aseg + s->sh_size);
> +
> +		if (IS_ENABLED(CONFIG_EXPOLINE) &&
> +		    (!strcmp(".nospec_return_table", secname)))
> +			nospec_return_revert(aseg, aseg + s->sh_size);
>  	}
>  
>  	jump_label_apply_nops(me);
> diff --git a/arch/s390/kernel/nospec-branch.c b/arch/s390/kernel/nospec-branch.c
> new file mode 100644
> index 0000000..69d7fcf
> --- /dev/null
> +++ b/arch/s390/kernel/nospec-branch.c
> @@ -0,0 +1,100 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#include <linux/module.h>
> +#include <asm/nospec-branch.h>
> +
> +int nospec_call_disable = IS_ENABLED(EXPOLINE_OFF);
> +int nospec_return_disable = !IS_ENABLED(EXPOLINE_FULL);
> +
> +static int __init nospectre_v2_setup_early(char *str)
> +{
> +	nospec_call_disable = 1;
> +	nospec_return_disable = 1;
> +	return 0;
> +}
> +early_param("nospectre_v2", nospectre_v2_setup_early);
> +
> +static int __init spectre_v2_setup_early(char *str)
> +{
> +	if (str && !strncmp(str, "on", 2)) {
> +		nospec_call_disable = 0;
> +		nospec_return_disable = 0;
> +	}
> +	if (str && !strncmp(str, "off", 3)) {
> +		nospec_call_disable = 1;
> +		nospec_return_disable = 1;
> +	}
> +	if (str && !strncmp(str, "auto", 4)) {
> +		nospec_call_disable = 0;
> +		nospec_return_disable = 1;
> +	}
> +	return 0;
> +}
> +early_param("spectre_v2", spectre_v2_setup_early);
> +
> +static void __init_or_module __nospec_revert(s32 *start, s32 *end)
> +{
> +	enum { BRCL_EXPOLINE, BRASL_EXPOLINE } type;
> +	u8 *instr, *thunk, *br;
> +	u8 insnbuf[6];
> +	s32 *epo;
> +
> +	/* Second part of the instruction replace is always a nop */
> +	memcpy(insnbuf + 2, (char[]) { 0x47, 0x00, 0x00, 0x00 }, 4);
> +	for (epo = start; epo < end; epo++) {
> +		instr = (u8 *) epo + *epo;
> +		if (instr[0] == 0xc0 && (instr[1] & 0x0f) == 0x04)
> +			type = BRCL_EXPOLINE;	/* brcl instruction */
> +		else if (instr[0] == 0xc0 && (instr[1] & 0x0f) == 0x05)
> +			type = BRASL_EXPOLINE;	/* brasl instruction */
> +		else
> +			continue;
> +		thunk = instr + (*(int *)(instr + 2)) * 2;
> +		if (thunk[0] == 0xc6 && thunk[1] == 0x00)
> +			/* exrl %r0,<target-br> */
> +			br = thunk + (*(int *)(thunk + 2)) * 2;
> +		else if (thunk[0] == 0xc0 && (thunk[1] & 0x0f) == 0x00 &&
> +			 thunk[6] == 0x44 && thunk[7] == 0x00 &&
> +			 (thunk[8] & 0x0f) == 0x00 && thunk[9] == 0x00 &&
> +			 (thunk[1] & 0xf0) == (thunk[8] & 0xf0))
> +			/* larl %rx,<target br> + ex %r0,0(%rx) */
> +			br = thunk + (*(int *)(thunk + 2)) * 2;
> +		else
> +			continue;
> +		if (br[0] != 0x07 || (br[1] & 0xf0) != 0xf0)
> +			continue;
> +		switch (type) {
> +		case BRCL_EXPOLINE:
> +			/* brcl to thunk, replace with br + nop */
> +			insnbuf[0] = br[0];
> +			insnbuf[1] = (instr[1] & 0xf0) | (br[1] & 0x0f);
> +			break;
> +		case BRASL_EXPOLINE:
> +			/* brasl to thunk, replace with basr + nop */
> +			insnbuf[0] = 0x0d;
> +			insnbuf[1] = (instr[1] & 0xf0) | (br[1] & 0x0f);
> +			break;
> +		}
> +
> +		s390_kernel_write(instr, insnbuf, 6);
> +	}
> +}
> +
> +void __init_or_module nospec_call_revert(s32 *start, s32 *end)
> +{
> +	if (nospec_call_disable)
> +		__nospec_revert(start, end);
> +}
> +
> +void __init_or_module nospec_return_revert(s32 *start, s32 *end)
> +{
> +	if (nospec_return_disable)
> +		__nospec_revert(start, end);
> +}
> +
> +extern s32 __nospec_call_start[], __nospec_call_end[];
> +extern s32 __nospec_return_start[], __nospec_return_end[];
> +void __init nospec_init_branches(void)
> +{
> +	nospec_call_revert(__nospec_call_start, __nospec_call_end);
> +	nospec_return_revert(__nospec_return_start, __nospec_return_end);
> +}
> diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c
> index bcd2a4a..a6a91f0 100644
> --- a/arch/s390/kernel/setup.c
> +++ b/arch/s390/kernel/setup.c
> @@ -68,6 +68,7 @@
>  #include <asm/sysinfo.h>
>  #include <asm/numa.h>
>  #include <asm/alternative.h>
> +#include <asm/nospec-branch.h>
>  #include "entry.h"
>  
>  /*
> @@ -379,6 +380,7 @@ static void __init setup_lowcore(void)
>  	lc->spinlock_index = 0;
>  	arch_spin_lock_setup(0);
>  #endif
> +	lc->br_r1_trampoline = 0x07f1;	/* br %r1 */
>  
>  	set_prefix((u32)(unsigned long) lc);
>  	lowcore_ptr[0] = lc;
> @@ -954,6 +956,8 @@ void __init setup_arch(char **cmdline_p)
>  	set_preferred_console();
>  
>  	apply_alternative_instructions();
> +	if (IS_ENABLED(CONFIG_EXPOLINE))
> +		nospec_init_branches();
>  
>  	/* Setup zfcpdump support */
>  	setup_zfcpdump();
> diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c
> index 2fd7d60..a4a9fe1 100644
> --- a/arch/s390/kernel/smp.c
> +++ b/arch/s390/kernel/smp.c
> @@ -214,6 +214,7 @@ static int pcpu_alloc_lowcore(struct pcpu *pcpu, int cpu)
>  	lc->cpu_nr = cpu;
>  	lc->spinlock_lockval = arch_spin_lockval(cpu);
>  	lc->spinlock_index = 0;
> +	lc->br_r1_trampoline = 0x07f1;	/* br %r1 */
>  	if (nmi_alloc_per_cpu(lc))
>  		goto out;
>  	if (vdso_alloc_per_cpu(lc))
> diff --git a/arch/s390/kernel/vmlinux.lds.S b/arch/s390/kernel/vmlinux.lds.S
> index 608cf29..08d12cf 100644
> --- a/arch/s390/kernel/vmlinux.lds.S
> +++ b/arch/s390/kernel/vmlinux.lds.S
> @@ -123,6 +123,20 @@ SECTIONS
>  		*(.altinstr_replacement)
>  	}
>  
> +	/*
> +	 * Table with the patch locations to undo expolines
> +	*/
> +	.nospec_call_table : {
> +		__nospec_call_start = . ;
> +		*(.s390_indirect*)
> +		__nospec_call_end = . ;
> +	}
> +	.nospec_return_table : {
> +		__nospec_return_start = . ;
> +		*(.s390_return*)
> +		__nospec_return_end = . ;
> +	}
> +
>  	/* early.c uses stsi, which requires page aligned data. */
>  	. = ALIGN(PAGE_SIZE);
>  	INIT_DATA_SECTION(0x100)
> diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile
> index 614b44e..a2b33a2 100644
> --- a/drivers/s390/char/Makefile
> +++ b/drivers/s390/char/Makefile
> @@ -19,6 +19,8 @@ endif
>  
>  CFLAGS_sclp_early_core.o		+= -D__NO_FORTIFY
>  
> +CFLAGS_REMOVE_sclp_early_core.o	+= $(CC_FLAGS_EXPOLINE)
> +
>  obj-y += ctrlchar.o keyboard.o defkeymap.o sclp.o sclp_rw.o sclp_quiesce.o \
>  	 sclp_cmd.o sclp_config.o sclp_cpi_sys.o sclp_ocf.o sclp_ctl.o \
>  	 sclp_early.o sclp_early_core.o

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

Attachment: signature.asc
Description: Digital signature


[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Kernel Development]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite Info]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Linux Media]     [Device Mapper]

  Powered by Linux