diff options
author | Tejun Heo <tj@kernel.org> | 2009-02-11 02:31:00 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-02-11 05:33:49 -0500 |
commit | 5c79d2a517a9905599d192db8ce77ab5f1a2faca (patch) | |
tree | 11f550a59b9b653fbd6c54b37effbf2d4a750fb5 | |
parent | 60a5317ff0f42dd313094b88f809f63041568b08 (diff) |
x86: fix x86_32 stack protector bugs
Impact: fix x86_32 stack protector
Brian Gerst found out that %gs was being initialized to stack_canary
instead of stack_canary - 20, which basically gave the same canary
value for all threads. Fixing this also exposed the following bugs.
* cpu_idle() didn't call boot_init_stack_canary()
* stack canary switching in switch_to() was being done too late making
the initial run of a new thread use the old stack canary value.
Fix all of them and while at it update comment in cpu_idle() about
calling boot_init_stack_canary().
Reported-by: Brian Gerst <brgerst@gmail.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r-- | arch/x86/include/asm/stackprotector.h | 2 | ||||
-rw-r--r-- | arch/x86/include/asm/system.h | 8 | ||||
-rw-r--r-- | arch/x86/kernel/head_32.S | 1 | ||||
-rw-r--r-- | arch/x86/kernel/process_32.c | 10 | ||||
-rw-r--r-- | arch/x86/kernel/process_64.c | 11 |
5 files changed, 20 insertions, 12 deletions
diff --git a/arch/x86/include/asm/stackprotector.h b/arch/x86/include/asm/stackprotector.h index fa7e5bd6fbe8..c2d742c6e15f 100644 --- a/arch/x86/include/asm/stackprotector.h +++ b/arch/x86/include/asm/stackprotector.h | |||
@@ -85,7 +85,7 @@ static __always_inline void boot_init_stack_canary(void) | |||
85 | static inline void setup_stack_canary_segment(int cpu) | 85 | static inline void setup_stack_canary_segment(int cpu) |
86 | { | 86 | { |
87 | #ifdef CONFIG_X86_32 | 87 | #ifdef CONFIG_X86_32 |
88 | unsigned long canary = (unsigned long)&per_cpu(stack_canary, cpu); | 88 | unsigned long canary = (unsigned long)&per_cpu(stack_canary, cpu) - 20; |
89 | struct desc_struct *gdt_table = get_cpu_gdt_table(cpu); | 89 | struct desc_struct *gdt_table = get_cpu_gdt_table(cpu); |
90 | struct desc_struct desc; | 90 | struct desc_struct desc; |
91 | 91 | ||
diff --git a/arch/x86/include/asm/system.h b/arch/x86/include/asm/system.h index 2692ee8ef031..7a80f72bec47 100644 --- a/arch/x86/include/asm/system.h +++ b/arch/x86/include/asm/system.h | |||
@@ -25,13 +25,11 @@ struct task_struct *__switch_to(struct task_struct *prev, | |||
25 | 25 | ||
26 | #ifdef CONFIG_CC_STACKPROTECTOR | 26 | #ifdef CONFIG_CC_STACKPROTECTOR |
27 | #define __switch_canary \ | 27 | #define __switch_canary \ |
28 | "movl "__percpu_arg([current_task])",%%ebx\n\t" \ | 28 | "movl %P[task_canary](%[next]), %%ebx\n\t" \ |
29 | "movl %P[task_canary](%%ebx),%%ebx\n\t" \ | 29 | "movl %%ebx, "__percpu_arg([stack_canary])"\n\t" |
30 | "movl %%ebx,"__percpu_arg([stack_canary])"\n\t" | ||
31 | #define __switch_canary_oparam \ | 30 | #define __switch_canary_oparam \ |
32 | , [stack_canary] "=m" (per_cpu_var(stack_canary)) | 31 | , [stack_canary] "=m" (per_cpu_var(stack_canary)) |
33 | #define __switch_canary_iparam \ | 32 | #define __switch_canary_iparam \ |
34 | , [current_task] "m" (per_cpu_var(current_task)) \ | ||
35 | , [task_canary] "i" (offsetof(struct task_struct, stack_canary)) | 33 | , [task_canary] "i" (offsetof(struct task_struct, stack_canary)) |
36 | #else /* CC_STACKPROTECTOR */ | 34 | #else /* CC_STACKPROTECTOR */ |
37 | #define __switch_canary | 35 | #define __switch_canary |
@@ -60,9 +58,9 @@ do { \ | |||
60 | "movl %[next_sp],%%esp\n\t" /* restore ESP */ \ | 58 | "movl %[next_sp],%%esp\n\t" /* restore ESP */ \ |
61 | "movl $1f,%[prev_ip]\n\t" /* save EIP */ \ | 59 | "movl $1f,%[prev_ip]\n\t" /* save EIP */ \ |
62 | "pushl %[next_ip]\n\t" /* restore EIP */ \ | 60 | "pushl %[next_ip]\n\t" /* restore EIP */ \ |
61 | __switch_canary \ | ||
63 | "jmp __switch_to\n" /* regparm call */ \ | 62 | "jmp __switch_to\n" /* regparm call */ \ |
64 | "1:\t" \ | 63 | "1:\t" \ |
65 | __switch_canary \ | ||
66 | "popl %%ebp\n\t" /* restore EBP */ \ | 64 | "popl %%ebp\n\t" /* restore EBP */ \ |
67 | "popfl\n" /* restore flags */ \ | 65 | "popfl\n" /* restore flags */ \ |
68 | \ | 66 | \ |
diff --git a/arch/x86/kernel/head_32.S b/arch/x86/kernel/head_32.S index 924e31615fb6..cf21fd0cf6ac 100644 --- a/arch/x86/kernel/head_32.S +++ b/arch/x86/kernel/head_32.S | |||
@@ -447,6 +447,7 @@ is386: movl $2,%ecx # set MP | |||
447 | jne 1f | 447 | jne 1f |
448 | movl $per_cpu__gdt_page,%eax | 448 | movl $per_cpu__gdt_page,%eax |
449 | movl $per_cpu__stack_canary,%ecx | 449 | movl $per_cpu__stack_canary,%ecx |
450 | subl $20, %ecx | ||
450 | movw %cx, 8 * GDT_ENTRY_STACK_CANARY + 2(%eax) | 451 | movw %cx, 8 * GDT_ENTRY_STACK_CANARY + 2(%eax) |
451 | shrl $16, %ecx | 452 | shrl $16, %ecx |
452 | movb %cl, 8 * GDT_ENTRY_STACK_CANARY + 4(%eax) | 453 | movb %cl, 8 * GDT_ENTRY_STACK_CANARY + 4(%eax) |
diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c index 9a62383e7c3c..b50604bb1e41 100644 --- a/arch/x86/kernel/process_32.c +++ b/arch/x86/kernel/process_32.c | |||
@@ -11,6 +11,7 @@ | |||
11 | 11 | ||
12 | #include <stdarg.h> | 12 | #include <stdarg.h> |
13 | 13 | ||
14 | #include <linux/stackprotector.h> | ||
14 | #include <linux/cpu.h> | 15 | #include <linux/cpu.h> |
15 | #include <linux/errno.h> | 16 | #include <linux/errno.h> |
16 | #include <linux/sched.h> | 17 | #include <linux/sched.h> |
@@ -91,6 +92,15 @@ void cpu_idle(void) | |||
91 | { | 92 | { |
92 | int cpu = smp_processor_id(); | 93 | int cpu = smp_processor_id(); |
93 | 94 | ||
95 | /* | ||
96 | * If we're the non-boot CPU, nothing set the stack canary up | ||
97 | * for us. CPU0 already has it initialized but no harm in | ||
98 | * doing it again. This is a good place for updating it, as | ||
99 | * we wont ever return from this function (so the invalid | ||
100 | * canaries already on the stack wont ever trigger). | ||
101 | */ | ||
102 | boot_init_stack_canary(); | ||
103 | |||
94 | current_thread_info()->status |= TS_POLLING; | 104 | current_thread_info()->status |= TS_POLLING; |
95 | 105 | ||
96 | /* endless idle loop with no priority at all */ | 106 | /* endless idle loop with no priority at all */ |
diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c index 8eb169e45584..836ef6575f01 100644 --- a/arch/x86/kernel/process_64.c +++ b/arch/x86/kernel/process_64.c | |||
@@ -120,12 +120,11 @@ void cpu_idle(void) | |||
120 | current_thread_info()->status |= TS_POLLING; | 120 | current_thread_info()->status |= TS_POLLING; |
121 | 121 | ||
122 | /* | 122 | /* |
123 | * If we're the non-boot CPU, nothing set the PDA stack | 123 | * If we're the non-boot CPU, nothing set the stack canary up |
124 | * canary up for us - and if we are the boot CPU we have | 124 | * for us. CPU0 already has it initialized but no harm in |
125 | * a 0 stack canary. This is a good place for updating | 125 | * doing it again. This is a good place for updating it, as |
126 | * it, as we wont ever return from this function (so the | 126 | * we wont ever return from this function (so the invalid |
127 | * invalid canaries already on the stack wont ever | 127 | * canaries already on the stack wont ever trigger). |
128 | * trigger): | ||
129 | */ | 128 | */ |
130 | boot_init_stack_canary(); | 129 | boot_init_stack_canary(); |
131 | 130 | ||