x86: fix x86_32 stack protector bugs
authorTejun Heo <tj@kernel.org>
Wed, 11 Feb 2009 07:31:00 +0000 (16:31 +0900)
committerIngo Molnar <mingo@elte.hu>
Wed, 11 Feb 2009 10:33:49 +0000 (11:33 +0100)
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>
arch/x86/include/asm/stackprotector.h
arch/x86/include/asm/system.h
arch/x86/kernel/head_32.S
arch/x86/kernel/process_32.c
arch/x86/kernel/process_64.c

index fa7e5bd..c2d742c 100644 (file)
@@ -85,7 +85,7 @@ static __always_inline void boot_init_stack_canary(void)
 static inline void setup_stack_canary_segment(int cpu)
 {
 #ifdef CONFIG_X86_32
-       unsigned long canary = (unsigned long)&per_cpu(stack_canary, cpu);
+       unsigned long canary = (unsigned long)&per_cpu(stack_canary, cpu) - 20;
        struct desc_struct *gdt_table = get_cpu_gdt_table(cpu);
        struct desc_struct desc;
 
index 2692ee8..7a80f72 100644 (file)
@@ -25,13 +25,11 @@ struct task_struct *__switch_to(struct task_struct *prev,
 
 #ifdef CONFIG_CC_STACKPROTECTOR
 #define __switch_canary                                                        \
-       "movl "__percpu_arg([current_task])",%%ebx\n\t"                 \
-       "movl %P[task_canary](%%ebx),%%ebx\n\t"                         \
-       "movl %%ebx,"__percpu_arg([stack_canary])"\n\t"
+       "movl %P[task_canary](%[next]), %%ebx\n\t"                      \
+       "movl %%ebx, "__percpu_arg([stack_canary])"\n\t"
 #define __switch_canary_oparam                                         \
        , [stack_canary] "=m" (per_cpu_var(stack_canary))
 #define __switch_canary_iparam                                         \
-       , [current_task] "m" (per_cpu_var(current_task))                \
        , [task_canary] "i" (offsetof(struct task_struct, stack_canary))
 #else  /* CC_STACKPROTECTOR */
 #define __switch_canary
@@ -60,9 +58,9 @@ do {                                                                  \
                     "movl %[next_sp],%%esp\n\t"        /* restore ESP   */ \
                     "movl $1f,%[prev_ip]\n\t"  /* save    EIP   */     \
                     "pushl %[next_ip]\n\t"     /* restore EIP   */     \
+                    __switch_canary                                    \
                     "jmp __switch_to\n"        /* regparm call  */     \
                     "1:\t"                                             \
-                    __switch_canary                                    \
                     "popl %%ebp\n\t"           /* restore EBP   */     \
                     "popfl\n"                  /* restore flags */     \
                                                                        \
index 924e316..cf21fd0 100644 (file)
@@ -447,6 +447,7 @@ is386:      movl $2,%ecx            # set MP
        jne 1f
        movl $per_cpu__gdt_page,%eax
        movl $per_cpu__stack_canary,%ecx
+       subl $20, %ecx
        movw %cx, 8 * GDT_ENTRY_STACK_CANARY + 2(%eax)
        shrl $16, %ecx
        movb %cl, 8 * GDT_ENTRY_STACK_CANARY + 4(%eax)
index 9a62383..b50604b 100644 (file)
@@ -11,6 +11,7 @@
 
 #include <stdarg.h>
 
+#include <linux/stackprotector.h>
 #include <linux/cpu.h>
 #include <linux/errno.h>
 #include <linux/sched.h>
@@ -91,6 +92,15 @@ void cpu_idle(void)
 {
        int cpu = smp_processor_id();
 
+       /*
+        * If we're the non-boot CPU, nothing set the stack canary up
+        * for us.  CPU0 already has it initialized but no harm in
+        * doing it again.  This is a good place for updating it, as
+        * we wont ever return from this function (so the invalid
+        * canaries already on the stack wont ever trigger).
+        */
+       boot_init_stack_canary();
+
        current_thread_info()->status |= TS_POLLING;
 
        /* endless idle loop with no priority at all */
index 8eb169e..836ef65 100644 (file)
@@ -120,12 +120,11 @@ void cpu_idle(void)
        current_thread_info()->status |= TS_POLLING;
 
        /*
-        * If we're the non-boot CPU, nothing set the PDA stack
-        * canary up for us - and if we are the boot CPU we have
-        * a 0 stack canary. This is a good place for updating
-        * it, as we wont ever return from this function (so the
-        * invalid canaries already on the stack wont ever
-        * trigger):
+        * If we're the non-boot CPU, nothing set the stack canary up
+        * for us.  CPU0 already has it initialized but no harm in
+        * doing it again.  This is a good place for updating it, as
+        * we wont ever return from this function (so the invalid
+        * canaries already on the stack wont ever trigger).
         */
        boot_init_stack_canary();