aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2009-02-09 08:17:40 -0500
committerIngo Molnar <mingo@elte.hu>2009-02-09 18:42:01 -0500
commit60a5317ff0f42dd313094b88f809f63041568b08 (patch)
tree307dfd9715fbc9ff83c3c3ae3b0e8f03888083f2 /arch/x86/kernel
parentccbeed3a05908d201b47b6c3dd1a373138bba566 (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/kernel')
-rw-r--r--arch/x86/kernel/Makefile18
-rw-r--r--arch/x86/kernel/cpu/common.c17
-rw-r--r--arch/x86/kernel/entry_32.S2
-rw-r--r--arch/x86/kernel/head_32.S20
-rw-r--r--arch/x86/kernel/process_32.c1
-rw-r--r--arch/x86/kernel/setup_percpu.c2
6 files changed, 52 insertions, 8 deletions
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index 37fa30bada17..b1f8be33300d 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -24,6 +24,24 @@ CFLAGS_vsyscall_64.o := $(PROFILING) -g0 $(nostackp)
24CFLAGS_hpet.o := $(nostackp) 24CFLAGS_hpet.o := $(nostackp)
25CFLAGS_tsc.o := $(nostackp) 25CFLAGS_tsc.o := $(nostackp)
26CFLAGS_paravirt.o := $(nostackp) 26CFLAGS_paravirt.o := $(nostackp)
27#
28# On x86_32, register frame is passed verbatim on stack as struct
29# pt_regs. gcc considers the parameter to belong to the callee and
30# with -fstack-protector it copies pt_regs to the callee's stack frame
31# to put the structure after the stack canary causing changes made by
32# the exception handlers to be lost. Turn off stack protector for all
33# files containing functions which take struct pt_regs from register
34# frame.
35#
36# The proper way to fix this is to teach gcc that the argument belongs
37# to the caller for these functions, oh well...
38#
39ifdef CONFIG_X86_32
40CFLAGS_process_32.o := $(nostackp)
41CFLAGS_vm86_32.o := $(nostackp)
42CFLAGS_signal.o := $(nostackp)
43CFLAGS_traps.o := $(nostackp)
44endif
27 45
28obj-y := process_$(BITS).o signal.o entry_$(BITS).o 46obj-y := process_$(BITS).o signal.o entry_$(BITS).o
29obj-y += traps.o irq.o irq_$(BITS).o dumpstack_$(BITS).o 47obj-y += traps.o irq.o irq_$(BITS).o dumpstack_$(BITS).o
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index 41b0de6df873..260fe4cb2c82 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -39,6 +39,7 @@
39#include <asm/sections.h> 39#include <asm/sections.h>
40#include <asm/setup.h> 40#include <asm/setup.h>
41#include <asm/hypervisor.h> 41#include <asm/hypervisor.h>
42#include <asm/stackprotector.h>
42 43
43#include "cpu.h" 44#include "cpu.h"
44 45
@@ -122,6 +123,7 @@ DEFINE_PER_CPU_PAGE_ALIGNED(struct gdt_page, gdt_page) = { .gdt = {
122 123
123 [GDT_ENTRY_ESPFIX_SS] = { { { 0x00000000, 0x00c09200 } } }, 124 [GDT_ENTRY_ESPFIX_SS] = { { { 0x00000000, 0x00c09200 } } },
124 [GDT_ENTRY_PERCPU] = { { { 0x0000ffff, 0x00cf9200 } } }, 125 [GDT_ENTRY_PERCPU] = { { { 0x0000ffff, 0x00cf9200 } } },
126 GDT_STACK_CANARY_INIT
125#endif 127#endif
126} }; 128} };
127EXPORT_PER_CPU_SYMBOL_GPL(gdt_page); 129EXPORT_PER_CPU_SYMBOL_GPL(gdt_page);
@@ -261,6 +263,7 @@ void load_percpu_segment(int cpu)
261 loadsegment(gs, 0); 263 loadsegment(gs, 0);
262 wrmsrl(MSR_GS_BASE, (unsigned long)per_cpu(irq_stack_union.gs_base, cpu)); 264 wrmsrl(MSR_GS_BASE, (unsigned long)per_cpu(irq_stack_union.gs_base, cpu));
263#endif 265#endif
266 load_stack_canary_segment();
264} 267}
265 268
266/* Current gdt points %fs at the "master" per-cpu area: after this, 269/* Current gdt points %fs at the "master" per-cpu area: after this,
@@ -946,16 +949,21 @@ unsigned long kernel_eflags;
946 */ 949 */
947DEFINE_PER_CPU(struct orig_ist, orig_ist); 950DEFINE_PER_CPU(struct orig_ist, orig_ist);
948 951
949#else 952#else /* x86_64 */
953
954#ifdef CONFIG_CC_STACKPROTECTOR
955DEFINE_PER_CPU(unsigned long, stack_canary);
956#endif
950 957
951/* Make sure %fs is initialized properly in idle threads */ 958/* Make sure %fs and %gs are initialized properly in idle threads */
952struct pt_regs * __cpuinit idle_regs(struct pt_regs *regs) 959struct pt_regs * __cpuinit idle_regs(struct pt_regs *regs)
953{ 960{
954 memset(regs, 0, sizeof(struct pt_regs)); 961 memset(regs, 0, sizeof(struct pt_regs));
955 regs->fs = __KERNEL_PERCPU; 962 regs->fs = __KERNEL_PERCPU;
963 regs->gs = __KERNEL_STACK_CANARY;
956 return regs; 964 return regs;
957} 965}
958#endif 966#endif /* x86_64 */
959 967
960/* 968/*
961 * cpu_init() initializes state that is per-CPU. Some data is already 969 * cpu_init() initializes state that is per-CPU. Some data is already
@@ -1120,9 +1128,6 @@ void __cpuinit cpu_init(void)
1120 __set_tss_desc(cpu, GDT_ENTRY_DOUBLEFAULT_TSS, &doublefault_tss); 1128 __set_tss_desc(cpu, GDT_ENTRY_DOUBLEFAULT_TSS, &doublefault_tss);
1121#endif 1129#endif
1122 1130
1123 /* Clear %gs. */
1124 asm volatile ("mov %0, %%gs" : : "r" (0));
1125
1126 /* Clear all 6 debug registers: */ 1131 /* Clear all 6 debug registers: */
1127 set_debugreg(0, 0); 1132 set_debugreg(0, 0);
1128 set_debugreg(0, 1); 1133 set_debugreg(0, 1);
diff --git a/arch/x86/kernel/entry_32.S b/arch/x86/kernel/entry_32.S
index 82e6868bee47..5f5bd22adcd4 100644
--- a/arch/x86/kernel/entry_32.S
+++ b/arch/x86/kernel/entry_32.S
@@ -186,7 +186,7 @@
186 /*CFI_REL_OFFSET gs, PT_GS*/ 186 /*CFI_REL_OFFSET gs, PT_GS*/
187.endm 187.endm
188.macro SET_KERNEL_GS reg 188.macro SET_KERNEL_GS reg
189 xorl \reg, \reg 189 movl $(__KERNEL_STACK_CANARY), \reg
190 movl \reg, %gs 190 movl \reg, %gs
191.endm 191.endm
192 192
diff --git a/arch/x86/kernel/head_32.S b/arch/x86/kernel/head_32.S
index 24c0e5cd71e3..924e31615fb6 100644
--- a/arch/x86/kernel/head_32.S
+++ b/arch/x86/kernel/head_32.S
@@ -19,6 +19,7 @@
19#include <asm/asm-offsets.h> 19#include <asm/asm-offsets.h>
20#include <asm/setup.h> 20#include <asm/setup.h>
21#include <asm/processor-flags.h> 21#include <asm/processor-flags.h>
22#include <asm/percpu.h>
22 23
23/* Physical address */ 24/* Physical address */
24#define pa(X) ((X) - __PAGE_OFFSET) 25#define pa(X) ((X) - __PAGE_OFFSET)
@@ -437,8 +438,25 @@ is386: movl $2,%ecx # set MP
437 movl $(__KERNEL_PERCPU), %eax 438 movl $(__KERNEL_PERCPU), %eax
438 movl %eax,%fs # set this cpu's percpu 439 movl %eax,%fs # set this cpu's percpu
439 440
440 xorl %eax,%eax # Clear GS and LDT 441#ifdef CONFIG_CC_STACKPROTECTOR
442 /*
443 * The linker can't handle this by relocation. Manually set
444 * base address in stack canary segment descriptor.
445 */
446 cmpb $0,ready
447 jne 1f
448 movl $per_cpu__gdt_page,%eax
449 movl $per_cpu__stack_canary,%ecx
450 movw %cx, 8 * GDT_ENTRY_STACK_CANARY + 2(%eax)
451 shrl $16, %ecx
452 movb %cl, 8 * GDT_ENTRY_STACK_CANARY + 4(%eax)
453 movb %ch, 8 * GDT_ENTRY_STACK_CANARY + 7(%eax)
4541:
455#endif
456 movl $(__KERNEL_STACK_CANARY),%eax
441 movl %eax,%gs 457 movl %eax,%gs
458
459 xorl %eax,%eax # Clear LDT
442 lldt %ax 460 lldt %ax
443 461
444 cld # gcc2 wants the direction flag cleared at all times 462 cld # gcc2 wants the direction flag cleared at all times
diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c
index 86122fa2a1ba..9a62383e7c3c 100644
--- a/arch/x86/kernel/process_32.c
+++ b/arch/x86/kernel/process_32.c
@@ -212,6 +212,7 @@ int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
212 regs.ds = __USER_DS; 212 regs.ds = __USER_DS;
213 regs.es = __USER_DS; 213 regs.es = __USER_DS;
214 regs.fs = __KERNEL_PERCPU; 214 regs.fs = __KERNEL_PERCPU;
215 regs.gs = __KERNEL_STACK_CANARY;
215 regs.orig_ax = -1; 216 regs.orig_ax = -1;
216 regs.ip = (unsigned long) kernel_thread_helper; 217 regs.ip = (unsigned long) kernel_thread_helper;
217 regs.cs = __KERNEL_CS | get_kernel_rpl(); 218 regs.cs = __KERNEL_CS | get_kernel_rpl();
diff --git a/arch/x86/kernel/setup_percpu.c b/arch/x86/kernel/setup_percpu.c
index ef91747bbed5..d992e6cff730 100644
--- a/arch/x86/kernel/setup_percpu.c
+++ b/arch/x86/kernel/setup_percpu.c
@@ -16,6 +16,7 @@
16#include <asm/proto.h> 16#include <asm/proto.h>
17#include <asm/cpumask.h> 17#include <asm/cpumask.h>
18#include <asm/cpu.h> 18#include <asm/cpu.h>
19#include <asm/stackprotector.h>
19 20
20#ifdef CONFIG_DEBUG_PER_CPU_MAPS 21#ifdef CONFIG_DEBUG_PER_CPU_MAPS
21# define DBG(x...) printk(KERN_DEBUG x) 22# define DBG(x...) printk(KERN_DEBUG x)
@@ -95,6 +96,7 @@ void __init setup_per_cpu_areas(void)
95 per_cpu(this_cpu_off, cpu) = per_cpu_offset(cpu); 96 per_cpu(this_cpu_off, cpu) = per_cpu_offset(cpu);
96 per_cpu(cpu_number, cpu) = cpu; 97 per_cpu(cpu_number, cpu) = cpu;
97 setup_percpu_segment(cpu); 98 setup_percpu_segment(cpu);
99 setup_stack_canary_segment(cpu);
98 /* 100 /*
99 * Copy data used in early init routines from the 101 * Copy data used in early init routines from the
100 * initial arrays to the per cpu data areas. These 102 * initial arrays to the per cpu data areas. These