Commit 3fb0fdb3 authored by Andy Lutomirski's avatar Andy Lutomirski Committed by Borislav Petkov
Browse files

x86/stackprotector/32: Make the canary into a regular percpu variable



On 32-bit kernels, the stackprotector canary is quite nasty -- it is
stored at %gs:(20), which is nasty because 32-bit kernels use %fs for
percpu storage.  It's even nastier because it means that whether %gs
contains userspace state or kernel state while running kernel code
depends on whether stackprotector is enabled (this is
CONFIG_X86_32_LAZY_GS), and this setting radically changes the way
that segment selectors work.  Supporting both variants is a
maintenance and testing mess.

Merely rearranging so that percpu and the stack canary
share the same segment would be messy as the 32-bit percpu address
layout isn't currently compatible with putting a variable at a fixed
offset.

Fortunately, GCC 8.1 added options that allow the stack canary to be
accessed as %fs:__stack_chk_guard, effectively turning it into an ordinary
percpu variable.  This lets us get rid of all of the code to manage the
stack canary GDT descriptor and the CONFIG_X86_32_LAZY_GS mess.

(That name is special.  We could use any symbol we want for the
 %fs-relative mode, but for CONFIG_SMP=n, gcc refuses to let us use any
 name other than __stack_chk_guard.)

Forcibly disable stackprotector on older compilers that don't support
the new options and turn the stack canary into a percpu variable. The
"lazy GS" approach is now used for all 32-bit configurations.

Also makes load_gs_index() work on 32-bit kernels. On 64-bit kernels,
it loads the GS selector and updates the user GSBASE accordingly. (This
is unchanged.) On 32-bit kernels, it loads the GS selector and updates
GSBASE, which is now always the user base. This means that the overall
effect is the same on 32-bit and 64-bit, which avoids some ifdeffery.

 [ bp: Massage commit message. ]

Signed-off-by: default avatarAndy Lutomirski <luto@kernel.org>
Signed-off-by: default avatarBorislav Petkov <bp@suse.de>
Link: https://lkml.kernel.org/r/c0ff7dba14041c7e5d1cae5d4df052f03759bef3.1613243844.git.luto@kernel.org
parent a38fd874
Loading
Loading
Loading
Loading
+2 −5
Original line number Diff line number Diff line
@@ -360,10 +360,6 @@ config X86_64_SMP
	def_bool y
	depends on X86_64 && SMP

config X86_32_LAZY_GS
	def_bool y
	depends on X86_32 && !STACKPROTECTOR

config ARCH_SUPPORTS_UPROBES
	def_bool y

@@ -386,7 +382,8 @@ config CC_HAS_SANE_STACKPROTECTOR
	default $(success,$(srctree)/scripts/gcc-x86_32-has-stack-protector.sh $(CC))
	help
	   We have to make sure stack protector is unconditionally disabled if
	   the compiler produces broken code.
	   the compiler produces broken code or if it does not let us control
	   the segment on 32-bit kernels.

menu "Processor type and features"

+8 −0
Original line number Diff line number Diff line
@@ -79,6 +79,14 @@ ifeq ($(CONFIG_X86_32),y)

        # temporary until string.h is fixed
        KBUILD_CFLAGS += -ffreestanding

	ifeq ($(CONFIG_STACKPROTECTOR),y)
		ifeq ($(CONFIG_SMP),y)
			KBUILD_CFLAGS += -mstack-protector-guard-reg=fs -mstack-protector-guard-symbol=__stack_chk_guard
		else
			KBUILD_CFLAGS += -mstack-protector-guard=global
		endif
	endif
else
        BITS := 64
        UTS_MACHINE := x86_64
+4 −52
Original line number Diff line number Diff line
@@ -20,7 +20,7 @@
 *	1C(%esp) - %ds
 *	20(%esp) - %es
 *	24(%esp) - %fs
 *	28(%esp) - %gs		saved iff !CONFIG_X86_32_LAZY_GS
 *	28(%esp) - unused -- was %gs on old stackprotector kernels
 *	2C(%esp) - orig_eax
 *	30(%esp) - %eip
 *	34(%esp) - %cs
@@ -56,14 +56,9 @@
/*
 * User gs save/restore
 *
 * %gs is used for userland TLS and kernel only uses it for stack
 * canary which is required to be at %gs:20 by gcc.  Read the comment
 * at the top of stackprotector.h for more info.
 *
 * Local labels 98 and 99 are used.
 * This is leftover junk from CONFIG_X86_32_LAZY_GS.  A subsequent patch
 * will remove it entirely.
 */
#ifdef CONFIG_X86_32_LAZY_GS

 /* unfortunately push/pop can't be no-op */
.macro PUSH_GS
	pushl	$0
@@ -86,49 +81,6 @@
.macro SET_KERNEL_GS reg
.endm

#else	/* CONFIG_X86_32_LAZY_GS */

.macro PUSH_GS
	pushl	%gs
.endm

.macro POP_GS pop=0
98:	popl	%gs
  .if \pop <> 0
	add	$\pop, %esp
  .endif
.endm
.macro POP_GS_EX
.pushsection .fixup, "ax"
99:	movl	$0, (%esp)
	jmp	98b
.popsection
	_ASM_EXTABLE(98b, 99b)
.endm

.macro PTGS_TO_GS
98:	mov	PT_GS(%esp), %gs
.endm
.macro PTGS_TO_GS_EX
.pushsection .fixup, "ax"
99:	movl	$0, PT_GS(%esp)
	jmp	98b
.popsection
	_ASM_EXTABLE(98b, 99b)
.endm

.macro GS_TO_REG reg
	movl	%gs, \reg
.endm
.macro REG_TO_PTGS reg
	movl	\reg, PT_GS(%esp)
.endm
.macro SET_KERNEL_GS reg
	movl	$(__KERNEL_STACK_CANARY), \reg
	movl	\reg, %gs
.endm

#endif /* CONFIG_X86_32_LAZY_GS */

/* Unconditionally switch to user cr3 */
.macro SWITCH_TO_USER_CR3 scratch_reg:req
@@ -779,7 +731,7 @@ SYM_CODE_START(__switch_to_asm)

#ifdef CONFIG_STACKPROTECTOR
	movl	TASK_stack_canary(%edx), %ebx
	movl	%ebx, PER_CPU_VAR(stack_canary)+stack_canary_offset
	movl	%ebx, PER_CPU_VAR(__stack_chk_guard)
#endif

#ifdef CONFIG_RETPOLINE
+4 −11
Original line number Diff line number Diff line
@@ -439,6 +439,9 @@ struct fixed_percpu_data {
	 * GCC hardcodes the stack canary as %gs:40.  Since the
	 * irq_stack is the object at %gs:0, we reserve the bottom
	 * 48 bytes of the irq stack for the canary.
	 *
	 * Once we are willing to require -mstack-protector-guard-symbol=
	 * support for x86_64 stackprotector, we can get rid of this.
	 */
	char		gs_base[40];
	unsigned long	stack_canary;
@@ -460,17 +463,7 @@ extern asmlinkage void ignore_sysret(void);
void current_save_fsgs(void);
#else	/* X86_64 */
#ifdef CONFIG_STACKPROTECTOR
/*
 * Make sure stack canary segment base is cached-aligned:
 *   "For Intel Atom processors, avoid non zero segment base address
 *    that is not aligned to cache line boundary at all cost."
 * (Optim Ref Manual Assembly/Compiler Coding Rule 15.)
 */
struct stack_canary {
	char __pad[20];		/* canary at %gs:20 */
	unsigned long canary;
};
DECLARE_PER_CPU_ALIGNED(struct stack_canary, stack_canary);
DECLARE_PER_CPU(unsigned long, __stack_chk_guard);
#endif
DECLARE_PER_CPU(struct irq_stack *, hardirq_stack_ptr);
DECLARE_PER_CPU(struct irq_stack *, softirq_stack_ptr);
+4 −1
Original line number Diff line number Diff line
@@ -37,7 +37,10 @@ struct pt_regs {
	unsigned short __esh;
	unsigned short fs;
	unsigned short __fsh;
	/* On interrupt, gs and __gsh store the vector number. */
	/*
	 * On interrupt, gs and __gsh store the vector number.  They never
	 * store gs any more.
	 */
	unsigned short gs;
	unsigned short __gsh;
	/* On interrupt, this is the error code. */
Loading