diff options
-rw-r--r-- | Makefile | 5 | ||||
-rw-r--r-- | Makefile.main | 11 | ||||
-rw-r--r-- | asm/asm-offsets.c | 4 | ||||
-rw-r--r-- | asm/head.S | 8 | ||||
-rw-r--r-- | asm/misc.S | 9 | ||||
-rw-r--r-- | core/Makefile.inc | 3 | ||||
-rw-r--r-- | core/backtrace.c | 2 | ||||
-rw-r--r-- | core/cpu.c | 23 | ||||
-rw-r--r-- | core/opal.c | 3 | ||||
-rw-r--r-- | core/utils.c | 90 | ||||
-rw-r--r-- | include/compiler.h | 2 | ||||
-rw-r--r-- | include/cpu.h | 7 | ||||
-rw-r--r-- | include/skiboot.h | 5 | ||||
-rw-r--r-- | include/stack.h | 12 |
14 files changed, 166 insertions, 18 deletions
@@ -20,6 +20,11 @@ PORE ?= 1 KERNEL ?= # +# Optional build with advanced stack checking +# +STACK_CHECK ?= 0 + +# # Where is the source directory, must be a full path (no ~) # Example: SRC= /home/me/skiboot # diff --git a/Makefile.main b/Makefile.main index 4df3cfd..aff145e 100644 --- a/Makefile.main +++ b/Makefile.main @@ -50,7 +50,16 @@ CPPFLAGS += -DBITS_PER_LONG=64 -m64 -DHAVE_BIG_ENDIAN # causing all our printf's to warn CPPFLAGS += -ffreestanding -CFLAGS := -fno-strict-aliasing -fno-stack-protector -pie +CFLAGS := -fno-strict-aliasing -fstack-protector-all -pie + +ifeq ($(STACK_CHECK),1) +CFLAGS += -fstack-protector-all -pg +CPPFLAGS += -DSTACK_CHECK_ENABLED +else +# XXX Add -fstack-protector-strong on gcc 4.9 +CFLAGS += -fstack-protector +endif + CFLAGS += $(CWARNS) $(OPTS) $(DBG) LDFLAGS := -m64 -static -nostdlib -Wl,--gc-sections -pie diff --git a/asm/asm-offsets.c b/asm/asm-offsets.c index e33c181..4fb2344 100644 --- a/asm/asm-offsets.c +++ b/asm/asm-offsets.c @@ -36,6 +36,10 @@ int main(void) OFFSET(CPUTHREAD_SAVE_R1, cpu_thread, save_r1); OFFSET(CPUTHREAD_STATE, cpu_thread, state); OFFSET(CPUTHREAD_CUR_TOKEN, cpu_thread, current_token); + DEFINE(CPUTHREAD_GAP, sizeof(struct cpu_thread) + STACK_SAFETY_GAP); + OFFSET(CPUTHREAD_STACK_BOT_MARK, cpu_thread, stack_bot_mark); + OFFSET(CPUTHREAD_STACK_BOT_PC, cpu_thread, stack_bot_pc); + OFFSET(CPUTHREAD_STACK_BOT_TOK, cpu_thread, stack_bot_tok); OFFSET(STACK_TYPE, stack_frame, type); OFFSET(STACK_LOCALS, stack_frame, locals); @@ -308,8 +308,14 @@ boot_entry: addi %r3,%r3,8 bdnz 1b - /* Jump to C */ + /* Get our per-cpu pointer into r13 */ GET_CPU() + + /* Initialize stack bottom mark to 0, it will be updated in C code */ + li %r0,0 + std %r0,CPUTHREAD_STACK_BOT_MARK(%r13) + + /* Jump to C */ mr %r3,%r27 mr %r4,%r25 bl main_cpu_entry @@ -17,6 +17,7 @@ #include <asm-utils.h> #include <asm-offsets.h> #include <processor.h> +#include <stack.h> .section ".text","ax" .balign 0x10 @@ -41,3 +42,11 @@ trigger_attn: isync attn blr + +#ifdef STACK_CHECK_ENABLED +.global _mcount +_mcount: + mr %r3,%r1 + mflr %r4 + b __mcount_stack_check +#endif diff --git a/core/Makefile.inc b/core/Makefile.inc index 475e2c7..07dfe75 100644 --- a/core/Makefile.inc +++ b/core/Makefile.inc @@ -10,4 +10,7 @@ CORE_OBJS += console-log.o ipmi.o time-utils.o pel.o pool.o errorlog.o CORE_OBJS += timer.o i2c.o CORE=core/built-in.o +CFLAGS_SKIP_core/relocate.o = -pg -fstack-protector-all +CFLAGS_SKIP_core/relocate.o += -fstack-protector -fstack-protector-strong + $(CORE): $(CORE_OBJS:%=core/%) diff --git a/core/backtrace.c b/core/backtrace.c index 2d05d3d..c4c8546 100644 --- a/core/backtrace.c +++ b/core/backtrace.c @@ -24,7 +24,7 @@ static char backtrace_buffer[STACK_BUF_SZ]; /* Dumps backtrace to buffer */ -void __backtrace(char *bt_buf, int bt_buf_len) +void __nomcount __backtrace(char *bt_buf, int bt_buf_len) { unsigned int pir = mfspr(SPR_PIR); unsigned long *sp; @@ -62,7 +62,17 @@ void __attrconst *cpu_stack_bottom(unsigned int pir) return (void *)&cpu_stacks[pir] + sizeof(struct cpu_thread); } -void cpu_relax(void) +void __attrconst *cpu_stack_top(unsigned int pir) +{ + /* This is the top of the MC stack which is above the normal + * stack, which means a SP between cpu_stack_bottom() and + * cpu_stack_top() can either be a normal stack pointer or + * a Machine Check stack pointer + */ + return (void *)&cpu_stacks[pir] + STACK_SIZE - STACK_TOP_GAP; +} + +void __nomcount cpu_relax(void) { /* Relax a bit to give sibling threads some breathing space */ smt_low(); @@ -74,16 +84,6 @@ void cpu_relax(void) smt_medium(); } -void __attrconst *cpu_stack_top(unsigned int pir) -{ - /* This is the top of the MC stack which is above the normal - * stack, which means a SP between cpu_stack_bottom() and - * cpu_stack_top() can either be a normal stack pointer or - * a Machine Check stack pointer - */ - return (void *)&cpu_stacks[pir] + STACK_SIZE - STACK_TOP_GAP; -} - struct cpu_job *__cpu_queue_job(struct cpu_thread *cpu, void (*func)(void *data), void *data, bool no_return) @@ -337,6 +337,7 @@ static void init_cpu_thread(struct cpu_thread *t, list_head_init(&t->job_queue); t->state = state; t->pir = pir; + t->stack_bot_mark = LONG_MAX; assert(pir == container_of(t, struct cpu_stack, cpu) - cpu_stacks); } diff --git a/core/opal.c b/core/opal.c index 5e1c742..7e53486 100644 --- a/core/opal.c +++ b/core/opal.c @@ -285,6 +285,9 @@ void opal_run_pollers(void) /* The pollers are run lokelessly, see comment in opal_del_poller */ list_for_each(&opal_pollers, poll_ent, link) poll_ent->poller(poll_ent->data); + + /* On debug builds, print max stack usage */ + check_stacks(); } static int64_t opal_poll_events(uint64_t *outstanding_event_mask) diff --git a/core/utils.c b/core/utils.c index 71f3842..1b1a8bf 100644 --- a/core/utils.c +++ b/core/utils.c @@ -18,14 +18,18 @@ #include <lock.h> #include <fsp.h> #include <processor.h> +#include <cpu.h> +#include <stack.h> -void assert_fail(const char *msg) +unsigned long __stack_chk_guard = 0xdeadf00dbaad300d; + +void __noreturn assert_fail(const char *msg) { prlog(PR_EMERG, "Assert fail: %s\n", msg); abort(); } -void abort(void) +void __noreturn abort(void) { static bool in_abort = false; unsigned long hid0; @@ -63,3 +67,85 @@ char __attrconst tohex(uint8_t nibble) return '?'; return __tohex[nibble]; } + +void __noreturn __nomcount __stack_chk_fail(void); +void __noreturn __nomcount __stack_chk_fail(void) +{ + prlog(PR_EMERG, "Stack corruption detected !\n"); + abort(); +} + +#ifdef STACK_CHECK_ENABLED + +void __nomcount __mcount_stack_check(uint64_t sp, uint64_t lr); +void __nomcount __mcount_stack_check(uint64_t sp, uint64_t lr) +{ + struct cpu_thread *c = this_cpu(); + uint64_t base = (uint64_t)c; + uint64_t bot = base + sizeof(struct cpu_thread); + int64_t mark = sp - bot; + uint64_t top = base + NORMAL_STACK_SIZE; + + /* + * Don't re-enter on this CPU or don't enter at all if somebody + * has spotted an overflow + */ + if (c->in_mcount) + return; + c->in_mcount = true; + + /* Capture lowest stack for this thread */ + if (mark < c->stack_bot_mark) { + c->stack_bot_mark = mark; + c->stack_bot_pc = lr; + c->stack_bot_tok = c->current_token; + } + + /* Stack is within bounds ? check for warning and bail */ + if (sp >= (bot + STACK_SAFETY_GAP) && sp < top) { + if (mark < STACK_WARNING_GAP) { + prlog(PR_EMERG, "CPU %04x Stack usage danger !" + " pc=%08llx sp=%08llx (gap=%lld) token=%lld\n", + c->pir, lr, sp, mark, c->current_token); + backtrace(); + } + c->in_mcount = false; + return; + } + + prlog(PR_EMERG, "CPU %04x Stack overflow detected !" + " pc=%08llx sp=%08llx (gap=%lld) token=%lld\n", + c->pir, lr, sp, mark, c->current_token); + abort(); +} + +static int64_t lowest_stack_mark = LONG_MAX; +static struct lock stack_check_lock = LOCK_UNLOCKED; + +void check_stacks(void) +{ + struct cpu_thread *c; + uint64_t lmark, lpc, ltok; + int found = -1; + + for_each_cpu(c) { + if (!c->stack_bot_mark || + c->stack_bot_mark >= lowest_stack_mark) + continue; + lock(&stack_check_lock); + if (c->stack_bot_mark >= lowest_stack_mark) { + unlock(&stack_check_lock); + continue; + } + lmark = lowest_stack_mark = c->stack_bot_mark; + lpc = c->stack_bot_pc; + ltok = c->stack_bot_tok; + found = c->pir; + unlock(&stack_check_lock); + } + if (found >= 0) + prlog(PR_NOTICE, "CPU %04x lowest stack mark %lld bytes left" + " pc=%08llx token=%lld\n", found, lmark, lpc, ltok); +} + +#endif /* STACK_CHECK_ENABLED */ diff --git a/include/compiler.h b/include/compiler.h index c1557d8..ab6e28e 100644 --- a/include/compiler.h +++ b/include/compiler.h @@ -35,6 +35,8 @@ #define offsetof(type,m) __builtin_offsetof(type,m) #endif +#define __nomcount __attribute__((no_instrument_function)) + /* Compiler barrier */ static inline void barrier(void) { diff --git a/include/cpu.h b/include/cpu.h index 7895e96..8cb17be 100644 --- a/include/cpu.h +++ b/include/cpu.h @@ -56,10 +56,13 @@ struct cpu_thread { void *icp_regs; uint32_t con_suspend; bool con_need_flush; + bool in_mcount; uint32_t hbrt_spec_wakeup; /* primary only */ uint64_t save_l2_fir_action1; uint64_t current_token; - + int64_t stack_bot_mark; + uint64_t stack_bot_pc; + uint64_t stack_bot_tok; struct lock job_lock; struct list_head job_queue; }; @@ -138,7 +141,7 @@ extern struct cpu_thread *next_available_core_in_chip(struct cpu_thread *cpu, u3 /* Return the caller CPU (only after init_cpu_threads) */ register struct cpu_thread *__this_cpu asm("r13"); -static inline struct cpu_thread *this_cpu(void) +static inline __nomcount struct cpu_thread *this_cpu(void) { return __this_cpu; } diff --git a/include/skiboot.h b/include/skiboot.h index 1a1f96f..53660af 100644 --- a/include/skiboot.h +++ b/include/skiboot.h @@ -122,6 +122,11 @@ extern void *boot_stack_top; /* For use by debug code */ extern void backtrace(void); extern void __backtrace(char *bt_buf, int bt_buf_len); +#ifdef STACK_CHECK_ENABLED +extern void check_stacks(void); +#else +static inline void check_stacks(void) { } +#endif /* Convert a 4-bit number to a hex char */ extern char tohex(uint8_t nibble); diff --git a/include/stack.h b/include/stack.h index 6eedc01..57771b4 100644 --- a/include/stack.h +++ b/include/stack.h @@ -17,6 +17,8 @@ #ifndef __STACKFRAME_H #define __STACKFRAME_H +#include <mem-map.h> + #define STACK_ENTRY_OPAL_API 0 /* OPAL call */ #define STACK_ENTRY_MCHECK 0x0200 /* Machine check */ #define STACK_ENTRY_HMI 0x0e60 /* Hypervisor maintainance */ @@ -39,6 +41,16 @@ /* Offset to get to machine check CPU stacks */ #define CPU_MC_STACKS_OFFSET (CPU_STACKS_BASE + STACK_SIZE - STACK_TOP_GAP) +/* Gap below the stack. If our stack checker sees the stack below that + * gap, it will flag a stack overflow + */ +#define STACK_SAFETY_GAP 512 + +/* Warning threshold, if stack goes below that on mcount, print a + * warning + */ +#define STACK_WARNING_GAP 1024 + #ifndef __ASSEMBLY__ #include <stdint.h> |