diff options
author | Tejun Heo <tj@kernel.org> | 2009-02-09 08:17:40 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-02-09 18:42:01 -0500 |
commit | 60a5317ff0f42dd313094b88f809f63041568b08 (patch) | |
tree | 307dfd9715fbc9ff83c3c3ae3b0e8f03888083f2 /arch/x86/include/asm | |
parent | ccbeed3a05908d201b47b6c3dd1a373138bba566 (diff) |
x86: implement x86_32 stack protector
Impact: stack protector for x86_32
Implement stack protector for x86_32. GDT entry 28 is used for it.
It's set to point to stack_canary-20 and have the length of 24 bytes.
CONFIG_CC_STACKPROTECTOR turns off CONFIG_X86_32_LAZY_GS and sets %gs
to the stack canary segment on entry. As %gs is otherwise unused by
the kernel, the canary can be anywhere. It's defined as a percpu
variable.
x86_32 exception handlers take register frame on stack directly as
struct pt_regs. With -fstack-protector turned on, gcc copies the
whole structure after the stack canary and (of course) doesn't copy
back on return thus losing all changed. For now, -fno-stack-protector
is added to all files which contain those functions. We definitely
need something better.
Signed-off-by: Tejun Heo <tj@kernel.org>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/x86/include/asm')
-rw-r--r-- | arch/x86/include/asm/processor.h | 4 | ||||
-rw-r--r-- | arch/x86/include/asm/segment.h | 9 | ||||
-rw-r--r-- | arch/x86/include/asm/stackprotector.h | 91 | ||||
-rw-r--r-- | arch/x86/include/asm/system.h | 21 |
4 files changed, 119 insertions, 6 deletions
diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h index 9763eb700138..5a9472104253 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h | |||
@@ -396,7 +396,11 @@ DECLARE_PER_CPU(union irq_stack_union, irq_stack_union); | |||
396 | DECLARE_INIT_PER_CPU(irq_stack_union); | 396 | DECLARE_INIT_PER_CPU(irq_stack_union); |
397 | 397 | ||
398 | DECLARE_PER_CPU(char *, irq_stack_ptr); | 398 | DECLARE_PER_CPU(char *, irq_stack_ptr); |
399 | #else /* X86_64 */ | ||
400 | #ifdef CONFIG_CC_STACKPROTECTOR | ||
401 | DECLARE_PER_CPU(unsigned long, stack_canary); | ||
399 | #endif | 402 | #endif |
403 | #endif /* X86_64 */ | ||
400 | 404 | ||
401 | extern void print_cpu_info(struct cpuinfo_x86 *); | 405 | extern void print_cpu_info(struct cpuinfo_x86 *); |
402 | extern unsigned int xstate_size; | 406 | extern unsigned int xstate_size; |
diff --git a/arch/x86/include/asm/segment.h b/arch/x86/include/asm/segment.h index 1dc1b51ac623..14e0ed86a6f9 100644 --- a/arch/x86/include/asm/segment.h +++ b/arch/x86/include/asm/segment.h | |||
@@ -61,7 +61,7 @@ | |||
61 | * | 61 | * |
62 | * 26 - ESPFIX small SS | 62 | * 26 - ESPFIX small SS |
63 | * 27 - per-cpu [ offset to per-cpu data area ] | 63 | * 27 - per-cpu [ offset to per-cpu data area ] |
64 | * 28 - unused | 64 | * 28 - stack_canary-20 [ for stack protector ] |
65 | * 29 - unused | 65 | * 29 - unused |
66 | * 30 - unused | 66 | * 30 - unused |
67 | * 31 - TSS for double fault handler | 67 | * 31 - TSS for double fault handler |
@@ -95,6 +95,13 @@ | |||
95 | #define __KERNEL_PERCPU 0 | 95 | #define __KERNEL_PERCPU 0 |
96 | #endif | 96 | #endif |
97 | 97 | ||
98 | #define GDT_ENTRY_STACK_CANARY (GDT_ENTRY_KERNEL_BASE + 16) | ||
99 | #ifdef CONFIG_CC_STACKPROTECTOR | ||
100 | #define __KERNEL_STACK_CANARY (GDT_ENTRY_STACK_CANARY * 8) | ||
101 | #else | ||
102 | #define __KERNEL_STACK_CANARY 0 | ||
103 | #endif | ||
104 | |||
98 | #define GDT_ENTRY_DOUBLEFAULT_TSS 31 | 105 | #define GDT_ENTRY_DOUBLEFAULT_TSS 31 |
99 | 106 | ||
100 | /* | 107 | /* |
diff --git a/arch/x86/include/asm/stackprotector.h b/arch/x86/include/asm/stackprotector.h index ee275e9f48ab..fa7e5bd6fbe8 100644 --- a/arch/x86/include/asm/stackprotector.h +++ b/arch/x86/include/asm/stackprotector.h | |||
@@ -1,3 +1,35 @@ | |||
1 | /* | ||
2 | * GCC stack protector support. | ||
3 | * | ||
4 | * Stack protector works by putting predefined pattern at the start of | ||
5 | * the stack frame and verifying that it hasn't been overwritten when | ||
6 | * returning from the function. The pattern is called stack canary | ||
7 | * and unfortunately gcc requires it to be at a fixed offset from %gs. | ||
8 | * On x86_64, the offset is 40 bytes and on x86_32 20 bytes. x86_64 | ||
9 | * and x86_32 use segment registers differently and thus handles this | ||
10 | * requirement differently. | ||
11 | * | ||
12 | * On x86_64, %gs is shared by percpu area and stack canary. All | ||
13 | * percpu symbols are zero based and %gs points to the base of percpu | ||
14 | * area. The first occupant of the percpu area is always | ||
15 | * irq_stack_union which contains stack_canary at offset 40. Userland | ||
16 | * %gs is always saved and restored on kernel entry and exit using | ||
17 | * swapgs, so stack protector doesn't add any complexity there. | ||
18 | * | ||
19 | * On x86_32, it's slightly more complicated. As in x86_64, %gs is | ||
20 | * used for userland TLS. Unfortunately, some processors are much | ||
21 | * slower at loading segment registers with different value when | ||
22 | * entering and leaving the kernel, so the kernel uses %fs for percpu | ||
23 | * area and manages %gs lazily so that %gs is switched only when | ||
24 | * necessary, usually during task switch. | ||
25 | * | ||
26 | * As gcc requires the stack canary at %gs:20, %gs can't be managed | ||
27 | * lazily if stack protector is enabled, so the kernel saves and | ||
28 | * restores userland %gs on kernel entry and exit. This behavior is | ||
29 | * controlled by CONFIG_X86_32_LAZY_GS and accessors are defined in | ||
30 | * system.h to hide the details. | ||
31 | */ | ||
32 | |||
1 | #ifndef _ASM_STACKPROTECTOR_H | 33 | #ifndef _ASM_STACKPROTECTOR_H |
2 | #define _ASM_STACKPROTECTOR_H 1 | 34 | #define _ASM_STACKPROTECTOR_H 1 |
3 | 35 | ||
@@ -6,9 +38,19 @@ | |||
6 | #include <asm/tsc.h> | 38 | #include <asm/tsc.h> |
7 | #include <asm/processor.h> | 39 | #include <asm/processor.h> |
8 | #include <asm/percpu.h> | 40 | #include <asm/percpu.h> |
41 | #include <asm/system.h> | ||
42 | #include <asm/desc.h> | ||
9 | #include <linux/random.h> | 43 | #include <linux/random.h> |
10 | 44 | ||
11 | /* | 45 | /* |
46 | * 24 byte read-only segment initializer for stack canary. Linker | ||
47 | * can't handle the address bit shifting. Address will be set in | ||
48 | * head_32 for boot CPU and setup_per_cpu_areas() for others. | ||
49 | */ | ||
50 | #define GDT_STACK_CANARY_INIT \ | ||
51 | [GDT_ENTRY_STACK_CANARY] = { { { 0x00000018, 0x00409000 } } }, | ||
52 | |||
53 | /* | ||
12 | * Initialize the stackprotector canary value. | 54 | * Initialize the stackprotector canary value. |
13 | * | 55 | * |
14 | * NOTE: this must only be called from functions that never return, | 56 | * NOTE: this must only be called from functions that never return, |
@@ -19,12 +61,9 @@ static __always_inline void boot_init_stack_canary(void) | |||
19 | u64 canary; | 61 | u64 canary; |
20 | u64 tsc; | 62 | u64 tsc; |
21 | 63 | ||
22 | /* | 64 | #ifdef CONFIG_X86_64 |
23 | * Build time only check to make sure the stack_canary is at | ||
24 | * offset 40 in the pda; this is a gcc ABI requirement | ||
25 | */ | ||
26 | BUILD_BUG_ON(offsetof(union irq_stack_union, stack_canary) != 40); | 65 | BUILD_BUG_ON(offsetof(union irq_stack_union, stack_canary) != 40); |
27 | 66 | #endif | |
28 | /* | 67 | /* |
29 | * We both use the random pool and the current TSC as a source | 68 | * We both use the random pool and the current TSC as a source |
30 | * of randomness. The TSC only matters for very early init, | 69 | * of randomness. The TSC only matters for very early init, |
@@ -36,7 +75,49 @@ static __always_inline void boot_init_stack_canary(void) | |||
36 | canary += tsc + (tsc << 32UL); | 75 | canary += tsc + (tsc << 32UL); |
37 | 76 | ||
38 | current->stack_canary = canary; | 77 | current->stack_canary = canary; |
78 | #ifdef CONFIG_X86_64 | ||
39 | percpu_write(irq_stack_union.stack_canary, canary); | 79 | percpu_write(irq_stack_union.stack_canary, canary); |
80 | #else | ||
81 | percpu_write(stack_canary, canary); | ||
82 | #endif | ||
83 | } | ||
84 | |||
85 | static inline void setup_stack_canary_segment(int cpu) | ||
86 | { | ||
87 | #ifdef CONFIG_X86_32 | ||
88 | unsigned long canary = (unsigned long)&per_cpu(stack_canary, cpu); | ||
89 | struct desc_struct *gdt_table = get_cpu_gdt_table(cpu); | ||
90 | struct desc_struct desc; | ||
91 | |||
92 | desc = gdt_table[GDT_ENTRY_STACK_CANARY]; | ||
93 | desc.base0 = canary & 0xffff; | ||
94 | desc.base1 = (canary >> 16) & 0xff; | ||
95 | desc.base2 = (canary >> 24) & 0xff; | ||
96 | write_gdt_entry(gdt_table, GDT_ENTRY_STACK_CANARY, &desc, DESCTYPE_S); | ||
97 | #endif | ||
98 | } | ||
99 | |||
100 | static inline void load_stack_canary_segment(void) | ||
101 | { | ||
102 | #ifdef CONFIG_X86_32 | ||
103 | asm("mov %0, %%gs" : : "r" (__KERNEL_STACK_CANARY) : "memory"); | ||
104 | #endif | ||
105 | } | ||
106 | |||
107 | #else /* CC_STACKPROTECTOR */ | ||
108 | |||
109 | #define GDT_STACK_CANARY_INIT | ||
110 | |||
111 | /* dummy boot_init_stack_canary() is defined in linux/stackprotector.h */ | ||
112 | |||
113 | static inline void setup_stack_canary_segment(int cpu) | ||
114 | { } | ||
115 | |||
116 | static inline void load_stack_canary_segment(void) | ||
117 | { | ||
118 | #ifdef CONFIG_X86_32 | ||
119 | asm volatile ("mov %0, %%gs" : : "r" (0)); | ||
120 | #endif | ||
40 | } | 121 | } |
41 | 122 | ||
42 | #endif /* CC_STACKPROTECTOR */ | 123 | #endif /* CC_STACKPROTECTOR */ |
diff --git a/arch/x86/include/asm/system.h b/arch/x86/include/asm/system.h index 79b98e5b96f4..2692ee8ef031 100644 --- a/arch/x86/include/asm/system.h +++ b/arch/x86/include/asm/system.h | |||
@@ -23,6 +23,22 @@ struct task_struct *__switch_to(struct task_struct *prev, | |||
23 | 23 | ||
24 | #ifdef CONFIG_X86_32 | 24 | #ifdef CONFIG_X86_32 |
25 | 25 | ||
26 | #ifdef CONFIG_CC_STACKPROTECTOR | ||
27 | #define __switch_canary \ | ||
28 | "movl "__percpu_arg([current_task])",%%ebx\n\t" \ | ||
29 | "movl %P[task_canary](%%ebx),%%ebx\n\t" \ | ||
30 | "movl %%ebx,"__percpu_arg([stack_canary])"\n\t" | ||
31 | #define __switch_canary_oparam \ | ||
32 | , [stack_canary] "=m" (per_cpu_var(stack_canary)) | ||
33 | #define __switch_canary_iparam \ | ||
34 | , [current_task] "m" (per_cpu_var(current_task)) \ | ||
35 | , [task_canary] "i" (offsetof(struct task_struct, stack_canary)) | ||
36 | #else /* CC_STACKPROTECTOR */ | ||
37 | #define __switch_canary | ||
38 | #define __switch_canary_oparam | ||
39 | #define __switch_canary_iparam | ||
40 | #endif /* CC_STACKPROTECTOR */ | ||
41 | |||
26 | /* | 42 | /* |
27 | * Saving eflags is important. It switches not only IOPL between tasks, | 43 | * Saving eflags is important. It switches not only IOPL between tasks, |
28 | * it also protects other tasks from NT leaking through sysenter etc. | 44 | * it also protects other tasks from NT leaking through sysenter etc. |
@@ -46,6 +62,7 @@ do { \ | |||
46 | "pushl %[next_ip]\n\t" /* restore EIP */ \ | 62 | "pushl %[next_ip]\n\t" /* restore EIP */ \ |
47 | "jmp __switch_to\n" /* regparm call */ \ | 63 | "jmp __switch_to\n" /* regparm call */ \ |
48 | "1:\t" \ | 64 | "1:\t" \ |
65 | __switch_canary \ | ||
49 | "popl %%ebp\n\t" /* restore EBP */ \ | 66 | "popl %%ebp\n\t" /* restore EBP */ \ |
50 | "popfl\n" /* restore flags */ \ | 67 | "popfl\n" /* restore flags */ \ |
51 | \ | 68 | \ |
@@ -58,6 +75,8 @@ do { \ | |||
58 | "=b" (ebx), "=c" (ecx), "=d" (edx), \ | 75 | "=b" (ebx), "=c" (ecx), "=d" (edx), \ |
59 | "=S" (esi), "=D" (edi) \ | 76 | "=S" (esi), "=D" (edi) \ |
60 | \ | 77 | \ |
78 | __switch_canary_oparam \ | ||
79 | \ | ||
61 | /* input parameters: */ \ | 80 | /* input parameters: */ \ |
62 | : [next_sp] "m" (next->thread.sp), \ | 81 | : [next_sp] "m" (next->thread.sp), \ |
63 | [next_ip] "m" (next->thread.ip), \ | 82 | [next_ip] "m" (next->thread.ip), \ |
@@ -66,6 +85,8 @@ do { \ | |||
66 | [prev] "a" (prev), \ | 85 | [prev] "a" (prev), \ |
67 | [next] "d" (next) \ | 86 | [next] "d" (next) \ |
68 | \ | 87 | \ |
88 | __switch_canary_iparam \ | ||
89 | \ | ||
69 | : /* reloaded segment registers */ \ | 90 | : /* reloaded segment registers */ \ |
70 | "memory"); \ | 91 | "memory"); \ |
71 | } while (0) | 92 | } while (0) |