From a51d4a641708759ecd27f407f8d1205ffdc39866 Mon Sep 17 00:00:00 2001 From: Marc Fiuczynski Date: Tue, 14 Dec 2004 19:13:38 +0000 Subject: [PATCH] This stack check implementation leverages the compiler's profiling (gcc -p) support to determine precisely when there is a stack overflow. The compiler generates a call to the special function 'mcount' in the prologue of each function. This special 'mcount' function checks whether the stack has grown beyond STACK_WARN size; and, if so, it will call the stack_overflowed function, which generates a stack trace. If the stack has grown to STACK_PANIC size, the stack_overflowed function will panic() the system. The hope is that with this support we can track down precisely the execution context when the stack overflows. --- Makefile | 6 ++- arch/i386/Kconfig | 25 ++++++++++ arch/i386/boot/compressed/misc.c | 3 ++ arch/i386/kernel/entry.S | 53 ++++++++++++++++++++++ arch/i386/kernel/i386_ksyms.c | 6 +++ arch/i386/kernel/init_task.c | 7 +++ arch/i386/kernel/process.c | 18 ++++++++ configs/kernel-2.6.8-i686-planetlab.config | 2 + include/asm-i386/thread_info.h | 1 + 9 files changed, 120 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d9f29b0bc..00c5f30c3 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 6 SUBLEVEL = 8 -EXTRAVERSION = -1.521.3.planetlab +EXTRAVERSION = -1.521.3.planetlab.2004.12.14 NAME=Zonked Quokka # *DOCUMENTATION* @@ -453,6 +453,10 @@ ifndef CONFIG_FRAME_POINTER CFLAGS += -fomit-frame-pointer endif +ifdef CONFIG_X86_STACK_CHECK +CFLAGS += -p +endif + ifdef CONFIG_DEBUG_INFO CFLAGS += -g endif diff --git a/arch/i386/Kconfig b/arch/i386/Kconfig index 4599ce62e..5922c842e 100644 --- a/arch/i386/Kconfig +++ b/arch/i386/Kconfig @@ -952,6 +952,31 @@ config STACK_WARN The kernel will print a stack trace when the current stack exceeds the specified size. +config X86_STACK_CHECK + bool "Check for stack overflows" + default n + help + Say Y here to have the kernel attempt to detect when the per-task + kernel stack overflows. + + Some older versions of gcc don't handle the -p option correctly. + Kernprof is affected by the same problem, which is described here: + http://oss.sgi.com/projects/kernprof/faq.html#Q9 + + Basically, if you get oopses in __free_pages_ok during boot when + you have this turned on, you need to fix gcc. The Redhat 2.96 + version and gcc-3.x seem to work. + + If not debugging a stack overflow problem, say N + +config STACK_PANIC + int "Panic when stack approaches with specified bytes of the stack limit" + depends on X86_STACK_CHECK + default 512 if IRQSTACKS + default 512 + help + Panic if the stack grows to within specified byte range. + endmenu diff --git a/arch/i386/boot/compressed/misc.c b/arch/i386/boot/compressed/misc.c index fa6704523..874568330 100644 --- a/arch/i386/boot/compressed/misc.c +++ b/arch/i386/boot/compressed/misc.c @@ -380,3 +380,6 @@ asmlinkage int decompress_kernel(struct moveparams *mv, void *rmode) if (high_loaded) close_output_buffer_if_we_run_high(mv); return high_loaded; } + +/* We don't actually check for stack overflows this early. */ +__asm__(".globl mcount ; mcount: ret\n"); diff --git a/arch/i386/kernel/entry.S b/arch/i386/kernel/entry.S index 3ac74183c..dc7ff8f75 100644 --- a/arch/i386/kernel/entry.S +++ b/arch/i386/kernel/entry.S @@ -734,9 +734,62 @@ ENTRY(spurious_interrupt_bug) pushl $do_spurious_interrupt_bug jmp error_code +#ifdef CONFIG_X86_STACK_CHECK +ENTRY(mcount) + push %eax + movl $(THREAD_SIZE - 1),%eax + andl %esp,%eax + cmpl $STACK_WARN,%eax /* esp reaches into STACK_WARN space */ + jle 1f +2: + popl %eax + ret +1: + lock; btsl $0,stack_overflowed + jc 2b + + # switch to overflow stack + movl %esp,%eax + movl $(stack_overflow_stack + THREAD_SIZE - 4),%esp + + pushf + cli + pushl %eax + + # push eip then esp of error for stack_overflow_panic + pushl 4(%eax) + pushl %eax + + # update the task pointer and cpu in the overflow stack's thread_info. + GET_THREAD_INFO_WITH_ESP(%eax) + movl TI_task(%eax),%ebx + movl %ebx,stack_overflow_stack+TI_task + movl TI_cpu(%eax),%ebx + movl %ebx,stack_overflow_stack+TI_cpu + + call stack_overflow + + # pop off call arguments + addl $8,%esp + + popl %eax + popf + movl %eax,%esp + popl %eax + movl $0,stack_overflowed + ret +#warning stack check enabled +#endif + .previous .data +#ifdef CONFIG_X86_STACK_CHECK + .globl stack_overflowed +stack_overflowed: + .long 0 +#endif + ENTRY(sys_call_table) .long sys_restart_syscall /* 0 - old "setup()" system call, used for restarting */ .long sys_exit diff --git a/arch/i386/kernel/i386_ksyms.c b/arch/i386/kernel/i386_ksyms.c index 5a50c536d..584982c3e 100644 --- a/arch/i386/kernel/i386_ksyms.c +++ b/arch/i386/kernel/i386_ksyms.c @@ -188,6 +188,12 @@ EXPORT_SYMBOL(atomic_dec_and_lock); EXPORT_SYMBOL(__PAGE_KERNEL); +#ifdef CONFIG_X86_STACK_CHECK +extern void mcount(void); +EXPORT_SYMBOL(mcount); +#endif + + #ifdef CONFIG_HIGHMEM EXPORT_SYMBOL(kmap); EXPORT_SYMBOL(kunmap); diff --git a/arch/i386/kernel/init_task.c b/arch/i386/kernel/init_task.c index 7422d73ee..30cfd4085 100644 --- a/arch/i386/kernel/init_task.c +++ b/arch/i386/kernel/init_task.c @@ -29,6 +29,13 @@ union thread_union init_thread_union __attribute__((__section__(".data.init_task"))) = { INIT_THREAD_INFO(init_task, init_thread_union) }; +#ifdef CONFIG_X86_STACK_CHECK +union thread_union stack_overflow_stack + __attribute__((__section__(".data.init_task"))) = + { INIT_THREAD_INFO(init_task, stack_overflow_stack) }; +#endif + + /* * Initial task structure. * diff --git a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c index 3093d1fc6..51c17cc52 100644 --- a/arch/i386/kernel/process.c +++ b/arch/i386/kernel/process.c @@ -219,6 +219,24 @@ static int __init idle_setup (char *str) __setup("idle=", idle_setup); +void stack_overflow(unsigned long esp, unsigned long eip) +{ + int panicing = ((esp&(THREAD_SIZE-1)) <= STACK_PANIC); + + printk( "esp: 0x%lx masked: 0x%lx STACK_PANIC:0x%lx %d %d\n", + esp, (esp&(THREAD_SIZE-1)), STACK_PANIC, (((esp&(THREAD_SIZE-1)) <= STACK_PANIC)), panicing ); + + if (panicing) + print_symbol("stack overflow from %s\n", eip); + else + print_symbol("excessive stack use from %s\n", eip); + printk("esp: %p\n", (void*)esp); + show_trace(current,(void*)esp); + + if (panicing) + panic("stack overflow\n"); +} + void show_regs(struct pt_regs * regs) { unsigned long cr0 = 0L, cr2 = 0L, cr3 = 0L, cr4 = 0L; diff --git a/configs/kernel-2.6.8-i686-planetlab.config b/configs/kernel-2.6.8-i686-planetlab.config index c23865ca3..f5a0c3079 100644 --- a/configs/kernel-2.6.8-i686-planetlab.config +++ b/configs/kernel-2.6.8-i686-planetlab.config @@ -144,6 +144,8 @@ CONFIG_REGPARM=y CONFIG_IRQSTACKS=y CONFIG_STACK_SIZE_SHIFT=13 CONFIG_STACK_WARN=4000 +# CONFIG_X86_STACK_CHECK is not set +CONFIG_STACK_PANIC=512 # # Power management options (ACPI, APM) diff --git a/include/asm-i386/thread_info.h b/include/asm-i386/thread_info.h index d4fe6a5f1..da74573aa 100644 --- a/include/asm-i386/thread_info.h +++ b/include/asm-i386/thread_info.h @@ -56,6 +56,7 @@ struct thread_info { #define PREEMPT_ACTIVE 0x4000000 #define THREAD_SIZE (1<