aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2014-11-17 18:22:04 +1100
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2014-11-17 18:22:04 +1100
commit71664fd8d2d2550a56cc6a9c2b81797bfe90d613 (patch)
tree6f55e009ff6a8b00f9f1c7a905ad4075225dec93
parent31d20fc02c8ee7c12d84bc28ce732bbc362ce369 (diff)
downloadskiboot-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>
-rw-r--r--Makefile5
-rw-r--r--Makefile.main11
-rw-r--r--asm/asm-offsets.c4
-rw-r--r--asm/head.S8
-rw-r--r--asm/misc.S9
-rw-r--r--core/Makefile.inc3
-rw-r--r--core/backtrace.c2
-rw-r--r--core/cpu.c23
-rw-r--r--core/opal.c3
-rw-r--r--core/utils.c90
-rw-r--r--include/compiler.h2
-rw-r--r--include/cpu.h7
-rw-r--r--include/skiboot.h5
-rw-r--r--include/stack.h12
14 files changed, 166 insertions, 18 deletions
diff --git a/Makefile b/Makefile
index b610222..39c97d5 100644
--- a/Makefile
+++ b/Makefile
@@ -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);
diff --git a/asm/head.S b/asm/head.S
index a56f2bb..75eb170 100644
--- a/asm/head.S
+++ b/asm/head.S
@@ -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
diff --git a/asm/misc.S b/asm/misc.S
index ccb30d1..00c7f78 100644
--- a/asm/misc.S
+++ b/asm/misc.S
@@ -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;
diff --git a/core/cpu.c b/core/cpu.c
index 8f203bb..aa046cc 100644
--- a/core/cpu.c
+++ b/core/cpu.c
@@ -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>