diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2014-11-17 18:22:04 +1100 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2014-11-17 18:22:04 +1100 |
commit | 71664fd8d2d2550a56cc6a9c2b81797bfe90d613 (patch) | |
tree | 6f55e009ff6a8b00f9f1c7a905ad4075225dec93 /core/utils.c | |
parent | 31d20fc02c8ee7c12d84bc28ce732bbc362ce369 (diff) | |
download | skiboot-71664fd8d2d2550a56cc6a9c2b81797bfe90d613.zip skiboot-71664fd8d2d2550a56cc6a9c2b81797bfe90d613.tar.gz skiboot-71664fd8d2d2550a56cc6a9c2b81797bfe90d613.tar.bz2 |
Stack checking extensions
This patch adds:
- Normal builds are done with -fstack-protector (we want to investigate
using -fstack-protector-strong on gcc4.9 but for now we just use that
- Build with STACK_CHECK=1 will use -fstack-protector-all and -pg and
will check the stack in mcount
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'core/utils.c')
-rw-r--r-- | core/utils.c | 90 |
1 files changed, 88 insertions, 2 deletions
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 */ |