summaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorHelge Deller <deller@gmx.de>2013-05-07 19:28:52 +0000
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-06-27 10:38:25 -0700
commit57c9e56523dcb65821d9cbd9382b1cb453ce2ad9 (patch)
tree16236dd0e7ea4f621fff5acb17ec1fed19480819 /arch
parentd92e6a30702e4c2268e6793ccfcb1e2fa5562eb1 (diff)
parisc: add kernel stack overflow check
commit 9372450cc22d185f708e5cc3557cf991be4b7dc5 upstream. Add the CONFIG_DEBUG_STACKOVERFLOW config option to enable checks to detect kernel stack overflows. Stack overflows can not be detected reliable since we do not want to introduce too much overhead. Instead, during irq processing in do_cpu_irq_mask() we check kernel stack usage of the interrupted kernel process. Kernel threads can be easily detected by checking the value of space register 7 (sr7) which is zero when running inside the kernel. Since THREAD_SIZE is 16k and PAGE_SIZE is 4k, reduce the alignment of the init thread to the lower value (PAGE_SIZE) in the kernel vmlinux.ld.S linker script. Signed-off-by: Helge Deller <deller@gmx.de> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'arch')
-rw-r--r--arch/parisc/Kconfig.debug11
-rw-r--r--arch/parisc/include/asm/thread_info.h2
-rw-r--r--arch/parisc/kernel/irq.c31
-rw-r--r--arch/parisc/kernel/vmlinux.lds.S2
4 files changed, 42 insertions, 4 deletions
diff --git a/arch/parisc/Kconfig.debug b/arch/parisc/Kconfig.debug
index 7305ac8f7f5b..eb0225f49ad7 100644
--- a/arch/parisc/Kconfig.debug
+++ b/arch/parisc/Kconfig.debug
@@ -27,3 +27,14 @@ config DEBUG_STRICT_USER_COPY_CHECKS
If unsure, or if you run an older (pre 4.4) gcc, say N.
endmenu
+
+config DEBUG_STACKOVERFLOW
+ bool "Check for stack overflows"
+ default y
+ depends on DEBUG_KERNEL
+ ---help---
+ Say Y here if you want to check the overflows of kernel, IRQ
+ and exception stacks. This option will cause messages of the
+ stacks in detail when free stack space drops below a certain
+ limit.
+ If in doubt, say "N".
diff --git a/arch/parisc/include/asm/thread_info.h b/arch/parisc/include/asm/thread_info.h
index d1fb79a36f3d..3ebe6c481428 100644
--- a/arch/parisc/include/asm/thread_info.h
+++ b/arch/parisc/include/asm/thread_info.h
@@ -40,7 +40,7 @@ struct thread_info {
/* thread information allocation */
-#define THREAD_SIZE_ORDER 2
+#define THREAD_SIZE_ORDER 2 /* PA-RISC requires at least 16k stack */
/* Be sure to hunt all references to this down when you change the size of
* the kernel stack */
#define THREAD_SIZE (PAGE_SIZE << THREAD_SIZE_ORDER)
diff --git a/arch/parisc/kernel/irq.c b/arch/parisc/kernel/irq.c
index 8094d3ed3b64..61e51ac85659 100644
--- a/arch/parisc/kernel/irq.c
+++ b/arch/parisc/kernel/irq.c
@@ -330,6 +330,34 @@ static inline int eirr_to_irq(unsigned long eirr)
return (BITS_PER_LONG - bit) + TIMER_IRQ;
}
+int sysctl_panic_on_stackoverflow = 1;
+
+static inline void stack_overflow_check(struct pt_regs *regs)
+{
+#ifdef CONFIG_DEBUG_STACKOVERFLOW
+ #define STACK_MARGIN (256*6)
+
+ /* Our stack starts directly behind the thread_info struct. */
+ unsigned long stack_start = (unsigned long) current_thread_info();
+ unsigned long sp = regs->gr[30];
+
+ /* if sr7 != 0, we interrupted a userspace process which we do not want
+ * to check for stack overflow. We will only check the kernel stack. */
+ if (regs->sr[7])
+ return;
+
+ if (likely((sp - stack_start) < (THREAD_SIZE - STACK_MARGIN)))
+ return;
+
+ pr_emerg("stackcheck: %s will most likely overflow kernel stack "
+ "(sp:%lx, stk bottom-top:%lx-%lx)\n",
+ current->comm, sp, stack_start, stack_start + THREAD_SIZE);
+
+ if (sysctl_panic_on_stackoverflow)
+ panic("low stack detected by irq handler - check messages\n");
+#endif
+}
+
/* ONLY called from entry.S:intr_extint() */
void do_cpu_irq_mask(struct pt_regs *regs)
{
@@ -364,6 +392,7 @@ void do_cpu_irq_mask(struct pt_regs *regs)
goto set_out;
}
#endif
+ stack_overflow_check(regs);
generic_handle_irq(irq);
out:
@@ -420,6 +449,4 @@ void __init init_IRQ(void)
cpu_eiem = EIEM_MASK(TIMER_IRQ);
#endif
set_eiem(cpu_eiem); /* EIEM : enable all external intr */
-
}
-
diff --git a/arch/parisc/kernel/vmlinux.lds.S b/arch/parisc/kernel/vmlinux.lds.S
index 64a999882e4f..4bb095a2f6fc 100644
--- a/arch/parisc/kernel/vmlinux.lds.S
+++ b/arch/parisc/kernel/vmlinux.lds.S
@@ -95,7 +95,7 @@ SECTIONS
NOTES
/* Data */
- RW_DATA_SECTION(L1_CACHE_BYTES, PAGE_SIZE, THREAD_SIZE)
+ RW_DATA_SECTION(L1_CACHE_BYTES, PAGE_SIZE, PAGE_SIZE)
/* PA-RISC locks requires 16-byte alignment */
. = ALIGN(16);