Skip to content

Commit 3fb0fdb

Browse files
amlutosuryasaimadhu
authored andcommitted
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: Andy Lutomirski <[email protected]> Signed-off-by: Borislav Petkov <[email protected]> Link: https://lkml.kernel.org/r/c0ff7dba14041c7e5d1cae5d4df052f03759bef3.1613243844.git.luto@kernel.org
1 parent a38fd87 commit 3fb0fdb

File tree

19 files changed

+60
-218
lines changed

19 files changed

+60
-218
lines changed

arch/x86/Kconfig

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -360,10 +360,6 @@ config X86_64_SMP
360360
def_bool y
361361
depends on X86_64 && SMP
362362

363-
config X86_32_LAZY_GS
364-
def_bool y
365-
depends on X86_32 && !STACKPROTECTOR
366-
367363
config ARCH_SUPPORTS_UPROBES
368364
def_bool y
369365

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

391388
menu "Processor type and features"
392389

arch/x86/Makefile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,14 @@ ifeq ($(CONFIG_X86_32),y)
7979

8080
# temporary until string.h is fixed
8181
KBUILD_CFLAGS += -ffreestanding
82+
83+
ifeq ($(CONFIG_STACKPROTECTOR),y)
84+
ifeq ($(CONFIG_SMP),y)
85+
KBUILD_CFLAGS += -mstack-protector-guard-reg=fs -mstack-protector-guard-symbol=__stack_chk_guard
86+
else
87+
KBUILD_CFLAGS += -mstack-protector-guard=global
88+
endif
89+
endif
8290
else
8391
BITS := 64
8492
UTS_MACHINE := x86_64

arch/x86/entry/entry_32.S

Lines changed: 4 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
* 1C(%esp) - %ds
2121
* 20(%esp) - %es
2222
* 24(%esp) - %fs
23-
* 28(%esp) - %gs saved iff !CONFIG_X86_32_LAZY_GS
23+
* 28(%esp) - unused -- was %gs on old stackprotector kernels
2424
* 2C(%esp) - orig_eax
2525
* 30(%esp) - %eip
2626
* 34(%esp) - %cs
@@ -56,14 +56,9 @@
5656
/*
5757
* User gs save/restore
5858
*
59-
* %gs is used for userland TLS and kernel only uses it for stack
60-
* canary which is required to be at %gs:20 by gcc. Read the comment
61-
* at the top of stackprotector.h for more info.
62-
*
63-
* Local labels 98 and 99 are used.
59+
* This is leftover junk from CONFIG_X86_32_LAZY_GS. A subsequent patch
60+
* will remove it entirely.
6461
*/
65-
#ifdef CONFIG_X86_32_LAZY_GS
66-
6762
/* unfortunately push/pop can't be no-op */
6863
.macro PUSH_GS
6964
pushl $0
@@ -86,49 +81,6 @@
8681
.macro SET_KERNEL_GS reg
8782
.endm
8883

89-
#else /* CONFIG_X86_32_LAZY_GS */
90-
91-
.macro PUSH_GS
92-
pushl %gs
93-
.endm
94-
95-
.macro POP_GS pop=0
96-
98: popl %gs
97-
.if \pop <> 0
98-
add $\pop, %esp
99-
.endif
100-
.endm
101-
.macro POP_GS_EX
102-
.pushsection .fixup, "ax"
103-
99: movl $0, (%esp)
104-
jmp 98b
105-
.popsection
106-
_ASM_EXTABLE(98b, 99b)
107-
.endm
108-
109-
.macro PTGS_TO_GS
110-
98: mov PT_GS(%esp), %gs
111-
.endm
112-
.macro PTGS_TO_GS_EX
113-
.pushsection .fixup, "ax"
114-
99: movl $0, PT_GS(%esp)
115-
jmp 98b
116-
.popsection
117-
_ASM_EXTABLE(98b, 99b)
118-
.endm
119-
120-
.macro GS_TO_REG reg
121-
movl %gs, \reg
122-
.endm
123-
.macro REG_TO_PTGS reg
124-
movl \reg, PT_GS(%esp)
125-
.endm
126-
.macro SET_KERNEL_GS reg
127-
movl $(__KERNEL_STACK_CANARY), \reg
128-
movl \reg, %gs
129-
.endm
130-
131-
#endif /* CONFIG_X86_32_LAZY_GS */
13284

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

780732
#ifdef CONFIG_STACKPROTECTOR
781733
movl TASK_stack_canary(%edx), %ebx
782-
movl %ebx, PER_CPU_VAR(stack_canary)+stack_canary_offset
734+
movl %ebx, PER_CPU_VAR(__stack_chk_guard)
783735
#endif
784736

785737
#ifdef CONFIG_RETPOLINE

arch/x86/include/asm/processor.h

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,9 @@ struct fixed_percpu_data {
439439
* GCC hardcodes the stack canary as %gs:40. Since the
440440
* irq_stack is the object at %gs:0, we reserve the bottom
441441
* 48 bytes of the irq stack for the canary.
442+
*
443+
* Once we are willing to require -mstack-protector-guard-symbol=
444+
* support for x86_64 stackprotector, we can get rid of this.
442445
*/
443446
char gs_base[40];
444447
unsigned long stack_canary;
@@ -460,17 +463,7 @@ extern asmlinkage void ignore_sysret(void);
460463
void current_save_fsgs(void);
461464
#else /* X86_64 */
462465
#ifdef CONFIG_STACKPROTECTOR
463-
/*
464-
* Make sure stack canary segment base is cached-aligned:
465-
* "For Intel Atom processors, avoid non zero segment base address
466-
* that is not aligned to cache line boundary at all cost."
467-
* (Optim Ref Manual Assembly/Compiler Coding Rule 15.)
468-
*/
469-
struct stack_canary {
470-
char __pad[20]; /* canary at %gs:20 */
471-
unsigned long canary;
472-
};
473-
DECLARE_PER_CPU_ALIGNED(struct stack_canary, stack_canary);
466+
DECLARE_PER_CPU(unsigned long, __stack_chk_guard);
474467
#endif
475468
DECLARE_PER_CPU(struct irq_stack *, hardirq_stack_ptr);
476469
DECLARE_PER_CPU(struct irq_stack *, softirq_stack_ptr);

arch/x86/include/asm/ptrace.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@ struct pt_regs {
3737
unsigned short __esh;
3838
unsigned short fs;
3939
unsigned short __fsh;
40-
/* On interrupt, gs and __gsh store the vector number. */
40+
/*
41+
* On interrupt, gs and __gsh store the vector number. They never
42+
* store gs any more.
43+
*/
4144
unsigned short gs;
4245
unsigned short __gsh;
4346
/* On interrupt, this is the error code. */

arch/x86/include/asm/segment.h

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@
9595
*
9696
* 26 - ESPFIX small SS
9797
* 27 - per-cpu [ offset to per-cpu data area ]
98-
* 28 - stack_canary-20 [ for stack protector ] <=== cacheline #8
98+
* 28 - unused
9999
* 29 - unused
100100
* 30 - unused
101101
* 31 - TSS for double fault handler
@@ -118,7 +118,6 @@
118118

119119
#define GDT_ENTRY_ESPFIX_SS 26
120120
#define GDT_ENTRY_PERCPU 27
121-
#define GDT_ENTRY_STACK_CANARY 28
122121

123122
#define GDT_ENTRY_DOUBLEFAULT_TSS 31
124123

@@ -158,12 +157,6 @@
158157
# define __KERNEL_PERCPU 0
159158
#endif
160159

161-
#ifdef CONFIG_STACKPROTECTOR
162-
# define __KERNEL_STACK_CANARY (GDT_ENTRY_STACK_CANARY*8)
163-
#else
164-
# define __KERNEL_STACK_CANARY 0
165-
#endif
166-
167160
#else /* 64-bit: */
168161

169162
#include <asm/cache.h>
@@ -364,22 +357,15 @@ static inline void __loadsegment_fs(unsigned short value)
364357
asm("mov %%" #seg ",%0":"=r" (value) : : "memory")
365358

366359
/*
367-
* x86-32 user GS accessors:
360+
* x86-32 user GS accessors. This is ugly and could do with some cleaning up.
368361
*/
369362
#ifdef CONFIG_X86_32
370-
# ifdef CONFIG_X86_32_LAZY_GS
371-
# define get_user_gs(regs) (u16)({ unsigned long v; savesegment(gs, v); v; })
372-
# define set_user_gs(regs, v) loadsegment(gs, (unsigned long)(v))
373-
# define task_user_gs(tsk) ((tsk)->thread.gs)
374-
# define lazy_save_gs(v) savesegment(gs, (v))
375-
# define lazy_load_gs(v) loadsegment(gs, (v))
376-
# else /* X86_32_LAZY_GS */
377-
# define get_user_gs(regs) (u16)((regs)->gs)
378-
# define set_user_gs(regs, v) do { (regs)->gs = (v); } while (0)
379-
# define task_user_gs(tsk) (task_pt_regs(tsk)->gs)
380-
# define lazy_save_gs(v) do { } while (0)
381-
# define lazy_load_gs(v) do { } while (0)
382-
# endif /* X86_32_LAZY_GS */
363+
# define get_user_gs(regs) (u16)({ unsigned long v; savesegment(gs, v); v; })
364+
# define set_user_gs(regs, v) loadsegment(gs, (unsigned long)(v))
365+
# define task_user_gs(tsk) ((tsk)->thread.gs)
366+
# define lazy_save_gs(v) savesegment(gs, (v))
367+
# define lazy_load_gs(v) loadsegment(gs, (v))
368+
# define load_gs_index(v) loadsegment(gs, (v))
383369
#endif /* X86_32 */
384370

385371
#endif /* !__ASSEMBLY__ */

arch/x86/include/asm/stackprotector.h

Lines changed: 16 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -5,30 +5,23 @@
55
* Stack protector works by putting predefined pattern at the start of
66
* the stack frame and verifying that it hasn't been overwritten when
77
* returning from the function. The pattern is called stack canary
8-
* and unfortunately gcc requires it to be at a fixed offset from %gs.
9-
* On x86_64, the offset is 40 bytes and on x86_32 20 bytes. x86_64
10-
* and x86_32 use segment registers differently and thus handles this
11-
* requirement differently.
8+
* and unfortunately gcc historically required it to be at a fixed offset
9+
* from the percpu segment base. On x86_64, the offset is 40 bytes.
1210
*
13-
* On x86_64, %gs is shared by percpu area and stack canary. All
14-
* percpu symbols are zero based and %gs points to the base of percpu
15-
* area. The first occupant of the percpu area is always
16-
* fixed_percpu_data which contains stack_canary at offset 40. Userland
17-
* %gs is always saved and restored on kernel entry and exit using
18-
* swapgs, so stack protector doesn't add any complexity there.
11+
* The same segment is shared by percpu area and stack canary. On
12+
* x86_64, percpu symbols are zero based and %gs (64-bit) points to the
13+
* base of percpu area. The first occupant of the percpu area is always
14+
* fixed_percpu_data which contains stack_canary at the approproate
15+
* offset. On x86_32, the stack canary is just a regular percpu
16+
* variable.
1917
*
20-
* On x86_32, it's slightly more complicated. As in x86_64, %gs is
21-
* used for userland TLS. Unfortunately, some processors are much
22-
* slower at loading segment registers with different value when
23-
* entering and leaving the kernel, so the kernel uses %fs for percpu
24-
* area and manages %gs lazily so that %gs is switched only when
25-
* necessary, usually during task switch.
18+
* Putting percpu data in %fs on 32-bit is a minor optimization compared to
19+
* using %gs. Since 32-bit userspace normally has %fs == 0, we are likely
20+
* to load 0 into %fs on exit to usermode, whereas with percpu data in
21+
* %gs, we are likely to load a non-null %gs on return to user mode.
2622
*
27-
* As gcc requires the stack canary at %gs:20, %gs can't be managed
28-
* lazily if stack protector is enabled, so the kernel saves and
29-
* restores userland %gs on kernel entry and exit. This behavior is
30-
* controlled by CONFIG_X86_32_LAZY_GS and accessors are defined in
31-
* system.h to hide the details.
23+
* Once we are willing to require GCC 8.1 or better for 64-bit stackprotector
24+
* support, we can remove some of this complexity.
3225
*/
3326

3427
#ifndef _ASM_STACKPROTECTOR_H
@@ -44,14 +37,6 @@
4437
#include <linux/random.h>
4538
#include <linux/sched.h>
4639

47-
/*
48-
* 24 byte read-only segment initializer for stack canary. Linker
49-
* can't handle the address bit shifting. Address will be set in
50-
* head_32 for boot CPU and setup_per_cpu_areas() for others.
51-
*/
52-
#define GDT_STACK_CANARY_INIT \
53-
[GDT_ENTRY_STACK_CANARY] = GDT_ENTRY_INIT(0x4090, 0, 0x18),
54-
5540
/*
5641
* Initialize the stackprotector canary value.
5742
*
@@ -86,7 +71,7 @@ static __always_inline void boot_init_stack_canary(void)
8671
#ifdef CONFIG_X86_64
8772
this_cpu_write(fixed_percpu_data.stack_canary, canary);
8873
#else
89-
this_cpu_write(stack_canary.canary, canary);
74+
this_cpu_write(__stack_chk_guard, canary);
9075
#endif
9176
}
9277

@@ -95,48 +80,16 @@ static inline void cpu_init_stack_canary(int cpu, struct task_struct *idle)
9580
#ifdef CONFIG_X86_64
9681
per_cpu(fixed_percpu_data.stack_canary, cpu) = idle->stack_canary;
9782
#else
98-
per_cpu(stack_canary.canary, cpu) = idle->stack_canary;
99-
#endif
100-
}
101-
102-
static inline void setup_stack_canary_segment(int cpu)
103-
{
104-
#ifdef CONFIG_X86_32
105-
unsigned long canary = (unsigned long)&per_cpu(stack_canary, cpu);
106-
struct desc_struct *gdt_table = get_cpu_gdt_rw(cpu);
107-
struct desc_struct desc;
108-
109-
desc = gdt_table[GDT_ENTRY_STACK_CANARY];
110-
set_desc_base(&desc, canary);
111-
write_gdt_entry(gdt_table, GDT_ENTRY_STACK_CANARY, &desc, DESCTYPE_S);
112-
#endif
113-
}
114-
115-
static inline void load_stack_canary_segment(void)
116-
{
117-
#ifdef CONFIG_X86_32
118-
asm("mov %0, %%gs" : : "r" (__KERNEL_STACK_CANARY) : "memory");
83+
per_cpu(__stack_chk_guard, cpu) = idle->stack_canary;
11984
#endif
12085
}
12186

12287
#else /* STACKPROTECTOR */
12388

124-
#define GDT_STACK_CANARY_INIT
125-
12689
/* dummy boot_init_stack_canary() is defined in linux/stackprotector.h */
12790

128-
static inline void setup_stack_canary_segment(int cpu)
129-
{ }
130-
13191
static inline void cpu_init_stack_canary(int cpu, struct task_struct *idle)
13292
{ }
13393

134-
static inline void load_stack_canary_segment(void)
135-
{
136-
#ifdef CONFIG_X86_32
137-
asm volatile ("mov %0, %%gs" : : "r" (0));
138-
#endif
139-
}
140-
14194
#endif /* STACKPROTECTOR */
14295
#endif /* _ASM_STACKPROTECTOR_H */

arch/x86/include/asm/suspend_32.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,10 @@
1313
/* image of the saved processor state */
1414
struct saved_context {
1515
/*
16-
* On x86_32, all segment registers, with the possible exception of
17-
* gs, are saved at kernel entry in pt_regs.
16+
* On x86_32, all segment registers except gs are saved at kernel
17+
* entry in pt_regs.
1818
*/
19-
#ifdef CONFIG_X86_32_LAZY_GS
2019
u16 gs;
21-
#endif
2220
unsigned long cr0, cr2, cr3, cr4;
2321
u64 misc_enable;
2422
bool misc_enable_saved;

arch/x86/kernel/asm-offsets_32.c

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,6 @@ void foo(void)
5353
offsetof(struct cpu_entry_area, tss.x86_tss.sp1) -
5454
offsetofend(struct cpu_entry_area, entry_stack_page.stack));
5555

56-
#ifdef CONFIG_STACKPROTECTOR
57-
BLANK();
58-
OFFSET(stack_canary_offset, stack_canary, canary);
59-
#endif
60-
6156
BLANK();
6257
DEFINE(EFI_svam, offsetof(efi_runtime_services_t, set_virtual_address_map));
6358
}

arch/x86/kernel/cpu/common.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,6 @@ DEFINE_PER_CPU_PAGE_ALIGNED(struct gdt_page, gdt_page) = { .gdt = {
161161

162162
[GDT_ENTRY_ESPFIX_SS] = GDT_ENTRY_INIT(0xc092, 0, 0xfffff),
163163
[GDT_ENTRY_PERCPU] = GDT_ENTRY_INIT(0xc092, 0, 0xfffff),
164-
GDT_STACK_CANARY_INIT
165164
#endif
166165
} };
167166
EXPORT_PER_CPU_SYMBOL_GPL(gdt_page);
@@ -599,7 +598,6 @@ void load_percpu_segment(int cpu)
599598
__loadsegment_simple(gs, 0);
600599
wrmsrl(MSR_GS_BASE, cpu_kernelmode_gs_base(cpu));
601600
#endif
602-
load_stack_canary_segment();
603601
}
604602

605603
#ifdef CONFIG_X86_32
@@ -1796,7 +1794,8 @@ DEFINE_PER_CPU(unsigned long, cpu_current_top_of_stack) =
17961794
EXPORT_PER_CPU_SYMBOL(cpu_current_top_of_stack);
17971795

17981796
#ifdef CONFIG_STACKPROTECTOR
1799-
DEFINE_PER_CPU_ALIGNED(struct stack_canary, stack_canary);
1797+
DEFINE_PER_CPU(unsigned long, __stack_chk_guard);
1798+
EXPORT_PER_CPU_SYMBOL(__stack_chk_guard);
18001799
#endif
18011800

18021801
#endif /* CONFIG_X86_64 */

0 commit comments

Comments
 (0)