All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH] riscv: add VMAP_STACK overflow detection
@ 2021-05-07  9:25 ` Tong Tiangen
  0 siblings, 0 replies; 9+ messages in thread
From: Tong Tiangen @ 2021-05-07  9:25 UTC (permalink / raw)
  To: Paul Walmsley, Palmer Dabbelt, Albert Ou
  Cc: linux-riscv, linux-kernel, Tong Tiangen

This patch adds stack overflow detection to riscv, usable when
CONFIG_VMAP_STACK=y.

Overflow is detected in kernel exception entry(kernel/entry.S), if the kernel
stack is overflow and been detected, the overflow handler is invoked on a
per-cpu overflow stack. This approach preserves GPRs and the original exception
information.

The overflow detect is performed before any attempt is made to access the stack
and the principle of stack overflow detection: kernel stacks are aligned to
double their size, enabling overflow to be detected with a single bit test. For
example, a 16K stack is aligned to 32K, ensuring that bit 14 of the SP must be
zero. On an overflow (or underflow), this bit is flipped. Thus, overflow (of
less than the size of the stack) can be detected by testing whether this bit is
set.

This gives us a useful error message on stack overflow, as can be trigger with
the LKDTM overflow test:

[  388.053267] lkdtm: Performing direct entry EXHAUST_STACK
[  388.053663] lkdtm: Calling function with 1024 frame size to depth 32 ...
[  388.054016] lkdtm: loop 32/32 ...
[  388.054186] lkdtm: loop 31/32 ...
[  388.054491] lkdtm: loop 30/32 ...
[  388.054672] lkdtm: loop 29/32 ...
[  388.054859] lkdtm: loop 28/32 ...
[  388.055010] lkdtm: loop 27/32 ...
[  388.055163] lkdtm: loop 26/32 ...
[  388.055309] lkdtm: loop 25/32 ...
[  388.055481] lkdtm: loop 24/32 ...
[  388.055653] lkdtm: loop 23/32 ...
[  388.055837] lkdtm: loop 22/32 ...
[  388.056015] lkdtm: loop 21/32 ...
[  388.056188] lkdtm: loop 20/32 ...
[  388.058145] Insufficient stack space to handle exception!
[  388.058153] Task stack:     [0xffffffd014260000..0xffffffd014264000]
[  388.058160] Overflow stack: [0xffffffe1f8d2c220..0xffffffe1f8d2d220]
[  388.058168] CPU: 0 PID: 89 Comm: bash Not tainted 5.12.0-rc8-dirty #90
[  388.058175] Hardware name: riscv-virtio,qemu (DT)
[  388.058187] epc : number+0x32/0x2c0
[  388.058247]  ra : vsnprintf+0x2ae/0x3f0
[  388.058255] epc : ffffffe0002d38f6 ra : ffffffe0002d814e sp : ffffffd01425ffc0
[  388.058263]  gp : ffffffe0012e4010 tp : ffffffe08014da00 t0 : ffffffd0142606e8
[  388.058271]  t1 : 0000000000000000 t2 : 0000000000000000 s0 : ffffffd014260070
[  388.058303]  s1 : ffffffd014260158 a0 : ffffffd01426015e a1 : ffffffd014260158
[  388.058311]  a2 : 0000000000000013 a3 : ffff0a01ffffff10 a4 : ffffffe000c398e0
[  388.058319]  a5 : 511b02ec65f3e300 a6 : 0000000000a1749a a7 : 0000000000000000
[  388.058327]  s2 : ffffffff000000ff s3 : 00000000ffff0a01 s4 : ffffffe0012e50a8
[  388.058335]  s5 : 0000000000ffff0a s6 : ffffffe0012e50a8 s7 : ffffffe000da1cc0
[  388.058343]  s8 : ffffffffffffffff s9 : ffffffd0142602b0 s10: ffffffd0142602a8
[  388.058351]  s11: ffffffd01426015e t3 : 00000000000f0000 t4 : ffffffffffffffff
[  388.058359]  t5 : 000000000000002f t6 : ffffffd014260158
[  388.058366] status: 0000000000000100 badaddr: ffffffd01425fff8 cause: 000000000000000f
[  388.058374] Kernel panic - not syncing: Kernel stack overflow
[  388.058381] CPU: 0 PID: 89 Comm: bash Not tainted 5.12.0-rc8-dirty #90
[  388.058387] Hardware name: riscv-virtio,qemu (DT)
[  388.058393] Call Trace:
[  388.058400] [<ffffffe000004944>] walk_stackframe+0x0/0xce
[  388.058406] [<ffffffe0006f0b28>] dump_backtrace+0x38/0x46
[  388.058412] [<ffffffe0006f0b46>] show_stack+0x10/0x18
[  388.058418] [<ffffffe0006f3690>] dump_stack+0x74/0x8e
[  388.058424] [<ffffffe0006f0d52>] panic+0xfc/0x2b2
[  388.058430] [<ffffffe0006f0acc>] print_trace_address+0x0/0x24
[  388.058436] [<ffffffe0002d814e>] vsnprintf+0x2ae/0x3f0
[  388.058956] SMP: stopping secondary CPUs

Signed-off-by: Tong Tiangen <tongtiangen@huawei.com>
---
 arch/riscv/Kconfig                   |   1 +
 arch/riscv/include/asm/thread_info.h |  15 ++++
 arch/riscv/kernel/entry.S            | 108 +++++++++++++++++++++++++++
 arch/riscv/kernel/traps.c            |  35 +++++++++
 4 files changed, 159 insertions(+)

diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index 4515a10c5d22..587f001e84f4 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -63,6 +63,7 @@ config RISCV
 	select HAVE_ARCH_MMAP_RND_BITS if MMU
 	select HAVE_ARCH_SECCOMP_FILTER
 	select HAVE_ARCH_TRACEHOOK
+	select HAVE_ARCH_VMAP_STACK
 	select HAVE_ASM_MODVERSIONS
 	select HAVE_CONTEXT_TRACKING
 	select HAVE_DEBUG_KMEMLEAK
diff --git a/arch/riscv/include/asm/thread_info.h b/arch/riscv/include/asm/thread_info.h
index 0e549a3089b3..60da0dcacf14 100644
--- a/arch/riscv/include/asm/thread_info.h
+++ b/arch/riscv/include/asm/thread_info.h
@@ -19,6 +19,21 @@
 #endif
 #define THREAD_SIZE		(PAGE_SIZE << THREAD_SIZE_ORDER)
 
+/*
+ * By aligning VMAP'd stacks to 2 * THREAD_SIZE, we can detect overflow by
+ * checking sp & (1 << THREAD_SHIFT), which we can do cheaply in the entry
+ * assembly.
+ */
+#ifdef CONFIG_VMAP_STACK
+#define THREAD_ALIGN            (2 * THREAD_SIZE)
+#else
+#define THREAD_ALIGN            THREAD_SIZE
+#endif
+
+#define THREAD_SHIFT            (PAGE_SHIFT + THREAD_SIZE_ORDER)
+#define OVERFLOW_STACK_SIZE     SZ_4K
+#define SHADOW_OVERFLOW_STACK_SIZE (1024)
+
 #ifndef __ASSEMBLY__
 
 #include <asm/processor.h>
diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S
index 83095faa680e..deadf4000b86 100644
--- a/arch/riscv/kernel/entry.S
+++ b/arch/riscv/kernel/entry.S
@@ -29,6 +29,15 @@ ENTRY(handle_exception)
 _restore_kernel_tpsp:
 	csrr tp, CSR_SCRATCH
 	REG_S sp, TASK_TI_KERNEL_SP(tp)
+
+#ifdef CONFIG_VMAP_STACK
+	addi sp, sp, -(PT_SIZE_ON_STACK)
+	srli sp, sp, THREAD_SHIFT
+	andi sp, sp, 0x1
+	bnez sp, handle_kernel_stack_overflow
+	REG_L sp, TASK_TI_KERNEL_SP(tp)
+#endif
+
 _save_context:
 	REG_S sp, TASK_TI_USER_SP(tp)
 	REG_L sp, TASK_TI_KERNEL_SP(tp)
@@ -375,6 +384,105 @@ handle_syscall_trace_exit:
 	call do_syscall_trace_exit
 	j ret_from_exception
 
+#ifdef CONFIG_VMAP_STACK
+handle_kernel_stack_overflow:
+	la sp, shadow_stack
+	addi sp, sp, SHADOW_OVERFLOW_STACK_SIZE
+
+	//save caller register to shadow stack
+	addi sp, sp, -(PT_SIZE_ON_STACK)
+	REG_S x1,  PT_RA(sp)
+	REG_S x5,  PT_T0(sp)
+	REG_S x6,  PT_T1(sp)
+	REG_S x7,  PT_T2(sp)
+	REG_S x10, PT_A0(sp)
+	REG_S x11, PT_A1(sp)
+	REG_S x12, PT_A2(sp)
+	REG_S x13, PT_A3(sp)
+	REG_S x14, PT_A4(sp)
+	REG_S x15, PT_A5(sp)
+	REG_S x16, PT_A6(sp)
+	REG_S x17, PT_A7(sp)
+	REG_S x28, PT_T3(sp)
+	REG_S x29, PT_T4(sp)
+	REG_S x30, PT_T5(sp)
+	REG_S x31, PT_T6(sp)
+
+	la ra, restore_caller_reg
+	tail get_overflow_stack
+
+restore_caller_reg:
+	//save per-cpu overflow stack
+	sd a0, -8(sp)
+	//restore caller register from shadow_stack
+	REG_L x1,  PT_RA(sp)
+	REG_L x5,  PT_T0(sp)
+	REG_L x6,  PT_T1(sp)
+	REG_L x7,  PT_T2(sp)
+	REG_L x10, PT_A0(sp)
+	REG_L x11, PT_A1(sp)
+	REG_L x12, PT_A2(sp)
+	REG_L x13, PT_A3(sp)
+	REG_L x14, PT_A4(sp)
+	REG_L x15, PT_A5(sp)
+	REG_L x16, PT_A6(sp)
+	REG_L x17, PT_A7(sp)
+	REG_L x28, PT_T3(sp)
+	REG_L x29, PT_T4(sp)
+	REG_L x30, PT_T5(sp)
+	REG_L x31, PT_T6(sp)
+
+	//load per-cpu overflow stack
+	ld sp, -8(sp)
+	addi sp, sp, -(PT_SIZE_ON_STACK)
+
+	//save context to overflow stack
+	REG_S x1,  PT_RA(sp)
+	REG_S x3,  PT_GP(sp)
+	REG_S x5,  PT_T0(sp)
+	REG_S x6,  PT_T1(sp)
+	REG_S x7,  PT_T2(sp)
+	REG_S x8,  PT_S0(sp)
+	REG_S x9,  PT_S1(sp)
+	REG_S x10, PT_A0(sp)
+	REG_S x11, PT_A1(sp)
+	REG_S x12, PT_A2(sp)
+	REG_S x13, PT_A3(sp)
+	REG_S x14, PT_A4(sp)
+	REG_S x15, PT_A5(sp)
+	REG_S x16, PT_A6(sp)
+	REG_S x17, PT_A7(sp)
+	REG_S x18, PT_S2(sp)
+	REG_S x19, PT_S3(sp)
+	REG_S x20, PT_S4(sp)
+	REG_S x21, PT_S5(sp)
+	REG_S x22, PT_S6(sp)
+	REG_S x23, PT_S7(sp)
+	REG_S x24, PT_S8(sp)
+	REG_S x25, PT_S9(sp)
+	REG_S x26, PT_S10(sp)
+	REG_S x27, PT_S11(sp)
+	REG_S x28, PT_T3(sp)
+	REG_S x29, PT_T4(sp)
+	REG_S x30, PT_T5(sp)
+	REG_S x31, PT_T6(sp)
+
+	REG_L s0, TASK_TI_KERNEL_SP(tp)
+	csrr s1, CSR_STATUS
+	csrr s2, CSR_EPC
+	csrr s3, CSR_TVAL
+	csrr s4, CSR_CAUSE
+	csrr s5, CSR_SCRATCH
+	REG_S s0, PT_SP(sp)
+	REG_S s1, PT_STATUS(sp)
+	REG_S s2, PT_EPC(sp)
+	REG_S s3, PT_BADADDR(sp)
+	REG_S s4, PT_CAUSE(sp)
+	REG_S s5, PT_TP(sp)
+	move a0, sp
+	tail handle_bad_stack
+#endif
+
 END(handle_exception)
 
 ENTRY(ret_from_fork)
diff --git a/arch/riscv/kernel/traps.c b/arch/riscv/kernel/traps.c
index 1357abf79570..ff8033b9fdd6 100644
--- a/arch/riscv/kernel/traps.c
+++ b/arch/riscv/kernel/traps.c
@@ -200,3 +200,38 @@ int is_valid_bugaddr(unsigned long pc)
 void trap_init(void)
 {
 }
+
+#ifdef CONFIG_VMAP_STACK
+DEFINE_PER_CPU(unsigned long [OVERFLOW_STACK_SIZE/sizeof(long)], overflow_stack)
+		__aligned(16);
+/*
+ * shadow stack, handled_ kernel_ stack_ overflow(in kernel/entry.S) is used
+ * to get per-cpu overflow stack(get_overflow_stack).
+ */
+long shadow_stack[SHADOW_OVERFLOW_STACK_SIZE/sizeof(long)];
+asmlinkage unsigned long get_overflow_stack(void)
+{
+	return (unsigned long)this_cpu_ptr(overflow_stack) +
+		OVERFLOW_STACK_SIZE;
+}
+
+asmlinkage void handle_bad_stack(struct pt_regs *regs)
+{
+	unsigned long tsk_stk = (unsigned long)current->stack;
+	unsigned long ovf_stk = (unsigned long)this_cpu_ptr(overflow_stack);
+
+	console_verbose();
+
+	pr_emerg("Insufficient stack space to handle exception!\n");
+	pr_emerg("Task stack:     [0x%016lx..0x%016lx]\n",
+			tsk_stk, tsk_stk + THREAD_SIZE);
+	pr_emerg("Overflow stack: [0x%016lx..0x%016lx]\n",
+			ovf_stk, ovf_stk + OVERFLOW_STACK_SIZE);
+
+	__show_regs(regs);
+	panic("Kernel stack overflow");
+
+	for (;;)
+		wait_for_interrupt();
+}
+#endif
-- 
2.18.0.huawei.25


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [RFC PATCH] riscv: add VMAP_STACK overflow detection
@ 2021-05-07  9:25 ` Tong Tiangen
  0 siblings, 0 replies; 9+ messages in thread
From: Tong Tiangen @ 2021-05-07  9:25 UTC (permalink / raw)
  To: Paul Walmsley, Palmer Dabbelt, Albert Ou
  Cc: linux-riscv, linux-kernel, Tong Tiangen

This patch adds stack overflow detection to riscv, usable when
CONFIG_VMAP_STACK=y.

Overflow is detected in kernel exception entry(kernel/entry.S), if the kernel
stack is overflow and been detected, the overflow handler is invoked on a
per-cpu overflow stack. This approach preserves GPRs and the original exception
information.

The overflow detect is performed before any attempt is made to access the stack
and the principle of stack overflow detection: kernel stacks are aligned to
double their size, enabling overflow to be detected with a single bit test. For
example, a 16K stack is aligned to 32K, ensuring that bit 14 of the SP must be
zero. On an overflow (or underflow), this bit is flipped. Thus, overflow (of
less than the size of the stack) can be detected by testing whether this bit is
set.

This gives us a useful error message on stack overflow, as can be trigger with
the LKDTM overflow test:

[  388.053267] lkdtm: Performing direct entry EXHAUST_STACK
[  388.053663] lkdtm: Calling function with 1024 frame size to depth 32 ...
[  388.054016] lkdtm: loop 32/32 ...
[  388.054186] lkdtm: loop 31/32 ...
[  388.054491] lkdtm: loop 30/32 ...
[  388.054672] lkdtm: loop 29/32 ...
[  388.054859] lkdtm: loop 28/32 ...
[  388.055010] lkdtm: loop 27/32 ...
[  388.055163] lkdtm: loop 26/32 ...
[  388.055309] lkdtm: loop 25/32 ...
[  388.055481] lkdtm: loop 24/32 ...
[  388.055653] lkdtm: loop 23/32 ...
[  388.055837] lkdtm: loop 22/32 ...
[  388.056015] lkdtm: loop 21/32 ...
[  388.056188] lkdtm: loop 20/32 ...
[  388.058145] Insufficient stack space to handle exception!
[  388.058153] Task stack:     [0xffffffd014260000..0xffffffd014264000]
[  388.058160] Overflow stack: [0xffffffe1f8d2c220..0xffffffe1f8d2d220]
[  388.058168] CPU: 0 PID: 89 Comm: bash Not tainted 5.12.0-rc8-dirty #90
[  388.058175] Hardware name: riscv-virtio,qemu (DT)
[  388.058187] epc : number+0x32/0x2c0
[  388.058247]  ra : vsnprintf+0x2ae/0x3f0
[  388.058255] epc : ffffffe0002d38f6 ra : ffffffe0002d814e sp : ffffffd01425ffc0
[  388.058263]  gp : ffffffe0012e4010 tp : ffffffe08014da00 t0 : ffffffd0142606e8
[  388.058271]  t1 : 0000000000000000 t2 : 0000000000000000 s0 : ffffffd014260070
[  388.058303]  s1 : ffffffd014260158 a0 : ffffffd01426015e a1 : ffffffd014260158
[  388.058311]  a2 : 0000000000000013 a3 : ffff0a01ffffff10 a4 : ffffffe000c398e0
[  388.058319]  a5 : 511b02ec65f3e300 a6 : 0000000000a1749a a7 : 0000000000000000
[  388.058327]  s2 : ffffffff000000ff s3 : 00000000ffff0a01 s4 : ffffffe0012e50a8
[  388.058335]  s5 : 0000000000ffff0a s6 : ffffffe0012e50a8 s7 : ffffffe000da1cc0
[  388.058343]  s8 : ffffffffffffffff s9 : ffffffd0142602b0 s10: ffffffd0142602a8
[  388.058351]  s11: ffffffd01426015e t3 : 00000000000f0000 t4 : ffffffffffffffff
[  388.058359]  t5 : 000000000000002f t6 : ffffffd014260158
[  388.058366] status: 0000000000000100 badaddr: ffffffd01425fff8 cause: 000000000000000f
[  388.058374] Kernel panic - not syncing: Kernel stack overflow
[  388.058381] CPU: 0 PID: 89 Comm: bash Not tainted 5.12.0-rc8-dirty #90
[  388.058387] Hardware name: riscv-virtio,qemu (DT)
[  388.058393] Call Trace:
[  388.058400] [<ffffffe000004944>] walk_stackframe+0x0/0xce
[  388.058406] [<ffffffe0006f0b28>] dump_backtrace+0x38/0x46
[  388.058412] [<ffffffe0006f0b46>] show_stack+0x10/0x18
[  388.058418] [<ffffffe0006f3690>] dump_stack+0x74/0x8e
[  388.058424] [<ffffffe0006f0d52>] panic+0xfc/0x2b2
[  388.058430] [<ffffffe0006f0acc>] print_trace_address+0x0/0x24
[  388.058436] [<ffffffe0002d814e>] vsnprintf+0x2ae/0x3f0
[  388.058956] SMP: stopping secondary CPUs

Signed-off-by: Tong Tiangen <tongtiangen@huawei.com>
---
 arch/riscv/Kconfig                   |   1 +
 arch/riscv/include/asm/thread_info.h |  15 ++++
 arch/riscv/kernel/entry.S            | 108 +++++++++++++++++++++++++++
 arch/riscv/kernel/traps.c            |  35 +++++++++
 4 files changed, 159 insertions(+)

diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index 4515a10c5d22..587f001e84f4 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -63,6 +63,7 @@ config RISCV
 	select HAVE_ARCH_MMAP_RND_BITS if MMU
 	select HAVE_ARCH_SECCOMP_FILTER
 	select HAVE_ARCH_TRACEHOOK
+	select HAVE_ARCH_VMAP_STACK
 	select HAVE_ASM_MODVERSIONS
 	select HAVE_CONTEXT_TRACKING
 	select HAVE_DEBUG_KMEMLEAK
diff --git a/arch/riscv/include/asm/thread_info.h b/arch/riscv/include/asm/thread_info.h
index 0e549a3089b3..60da0dcacf14 100644
--- a/arch/riscv/include/asm/thread_info.h
+++ b/arch/riscv/include/asm/thread_info.h
@@ -19,6 +19,21 @@
 #endif
 #define THREAD_SIZE		(PAGE_SIZE << THREAD_SIZE_ORDER)
 
+/*
+ * By aligning VMAP'd stacks to 2 * THREAD_SIZE, we can detect overflow by
+ * checking sp & (1 << THREAD_SHIFT), which we can do cheaply in the entry
+ * assembly.
+ */
+#ifdef CONFIG_VMAP_STACK
+#define THREAD_ALIGN            (2 * THREAD_SIZE)
+#else
+#define THREAD_ALIGN            THREAD_SIZE
+#endif
+
+#define THREAD_SHIFT            (PAGE_SHIFT + THREAD_SIZE_ORDER)
+#define OVERFLOW_STACK_SIZE     SZ_4K
+#define SHADOW_OVERFLOW_STACK_SIZE (1024)
+
 #ifndef __ASSEMBLY__
 
 #include <asm/processor.h>
diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S
index 83095faa680e..deadf4000b86 100644
--- a/arch/riscv/kernel/entry.S
+++ b/arch/riscv/kernel/entry.S
@@ -29,6 +29,15 @@ ENTRY(handle_exception)
 _restore_kernel_tpsp:
 	csrr tp, CSR_SCRATCH
 	REG_S sp, TASK_TI_KERNEL_SP(tp)
+
+#ifdef CONFIG_VMAP_STACK
+	addi sp, sp, -(PT_SIZE_ON_STACK)
+	srli sp, sp, THREAD_SHIFT
+	andi sp, sp, 0x1
+	bnez sp, handle_kernel_stack_overflow
+	REG_L sp, TASK_TI_KERNEL_SP(tp)
+#endif
+
 _save_context:
 	REG_S sp, TASK_TI_USER_SP(tp)
 	REG_L sp, TASK_TI_KERNEL_SP(tp)
@@ -375,6 +384,105 @@ handle_syscall_trace_exit:
 	call do_syscall_trace_exit
 	j ret_from_exception
 
+#ifdef CONFIG_VMAP_STACK
+handle_kernel_stack_overflow:
+	la sp, shadow_stack
+	addi sp, sp, SHADOW_OVERFLOW_STACK_SIZE
+
+	//save caller register to shadow stack
+	addi sp, sp, -(PT_SIZE_ON_STACK)
+	REG_S x1,  PT_RA(sp)
+	REG_S x5,  PT_T0(sp)
+	REG_S x6,  PT_T1(sp)
+	REG_S x7,  PT_T2(sp)
+	REG_S x10, PT_A0(sp)
+	REG_S x11, PT_A1(sp)
+	REG_S x12, PT_A2(sp)
+	REG_S x13, PT_A3(sp)
+	REG_S x14, PT_A4(sp)
+	REG_S x15, PT_A5(sp)
+	REG_S x16, PT_A6(sp)
+	REG_S x17, PT_A7(sp)
+	REG_S x28, PT_T3(sp)
+	REG_S x29, PT_T4(sp)
+	REG_S x30, PT_T5(sp)
+	REG_S x31, PT_T6(sp)
+
+	la ra, restore_caller_reg
+	tail get_overflow_stack
+
+restore_caller_reg:
+	//save per-cpu overflow stack
+	sd a0, -8(sp)
+	//restore caller register from shadow_stack
+	REG_L x1,  PT_RA(sp)
+	REG_L x5,  PT_T0(sp)
+	REG_L x6,  PT_T1(sp)
+	REG_L x7,  PT_T2(sp)
+	REG_L x10, PT_A0(sp)
+	REG_L x11, PT_A1(sp)
+	REG_L x12, PT_A2(sp)
+	REG_L x13, PT_A3(sp)
+	REG_L x14, PT_A4(sp)
+	REG_L x15, PT_A5(sp)
+	REG_L x16, PT_A6(sp)
+	REG_L x17, PT_A7(sp)
+	REG_L x28, PT_T3(sp)
+	REG_L x29, PT_T4(sp)
+	REG_L x30, PT_T5(sp)
+	REG_L x31, PT_T6(sp)
+
+	//load per-cpu overflow stack
+	ld sp, -8(sp)
+	addi sp, sp, -(PT_SIZE_ON_STACK)
+
+	//save context to overflow stack
+	REG_S x1,  PT_RA(sp)
+	REG_S x3,  PT_GP(sp)
+	REG_S x5,  PT_T0(sp)
+	REG_S x6,  PT_T1(sp)
+	REG_S x7,  PT_T2(sp)
+	REG_S x8,  PT_S0(sp)
+	REG_S x9,  PT_S1(sp)
+	REG_S x10, PT_A0(sp)
+	REG_S x11, PT_A1(sp)
+	REG_S x12, PT_A2(sp)
+	REG_S x13, PT_A3(sp)
+	REG_S x14, PT_A4(sp)
+	REG_S x15, PT_A5(sp)
+	REG_S x16, PT_A6(sp)
+	REG_S x17, PT_A7(sp)
+	REG_S x18, PT_S2(sp)
+	REG_S x19, PT_S3(sp)
+	REG_S x20, PT_S4(sp)
+	REG_S x21, PT_S5(sp)
+	REG_S x22, PT_S6(sp)
+	REG_S x23, PT_S7(sp)
+	REG_S x24, PT_S8(sp)
+	REG_S x25, PT_S9(sp)
+	REG_S x26, PT_S10(sp)
+	REG_S x27, PT_S11(sp)
+	REG_S x28, PT_T3(sp)
+	REG_S x29, PT_T4(sp)
+	REG_S x30, PT_T5(sp)
+	REG_S x31, PT_T6(sp)
+
+	REG_L s0, TASK_TI_KERNEL_SP(tp)
+	csrr s1, CSR_STATUS
+	csrr s2, CSR_EPC
+	csrr s3, CSR_TVAL
+	csrr s4, CSR_CAUSE
+	csrr s5, CSR_SCRATCH
+	REG_S s0, PT_SP(sp)
+	REG_S s1, PT_STATUS(sp)
+	REG_S s2, PT_EPC(sp)
+	REG_S s3, PT_BADADDR(sp)
+	REG_S s4, PT_CAUSE(sp)
+	REG_S s5, PT_TP(sp)
+	move a0, sp
+	tail handle_bad_stack
+#endif
+
 END(handle_exception)
 
 ENTRY(ret_from_fork)
diff --git a/arch/riscv/kernel/traps.c b/arch/riscv/kernel/traps.c
index 1357abf79570..ff8033b9fdd6 100644
--- a/arch/riscv/kernel/traps.c
+++ b/arch/riscv/kernel/traps.c
@@ -200,3 +200,38 @@ int is_valid_bugaddr(unsigned long pc)
 void trap_init(void)
 {
 }
+
+#ifdef CONFIG_VMAP_STACK
+DEFINE_PER_CPU(unsigned long [OVERFLOW_STACK_SIZE/sizeof(long)], overflow_stack)
+		__aligned(16);
+/*
+ * shadow stack, handled_ kernel_ stack_ overflow(in kernel/entry.S) is used
+ * to get per-cpu overflow stack(get_overflow_stack).
+ */
+long shadow_stack[SHADOW_OVERFLOW_STACK_SIZE/sizeof(long)];
+asmlinkage unsigned long get_overflow_stack(void)
+{
+	return (unsigned long)this_cpu_ptr(overflow_stack) +
+		OVERFLOW_STACK_SIZE;
+}
+
+asmlinkage void handle_bad_stack(struct pt_regs *regs)
+{
+	unsigned long tsk_stk = (unsigned long)current->stack;
+	unsigned long ovf_stk = (unsigned long)this_cpu_ptr(overflow_stack);
+
+	console_verbose();
+
+	pr_emerg("Insufficient stack space to handle exception!\n");
+	pr_emerg("Task stack:     [0x%016lx..0x%016lx]\n",
+			tsk_stk, tsk_stk + THREAD_SIZE);
+	pr_emerg("Overflow stack: [0x%016lx..0x%016lx]\n",
+			ovf_stk, ovf_stk + OVERFLOW_STACK_SIZE);
+
+	__show_regs(regs);
+	panic("Kernel stack overflow");
+
+	for (;;)
+		wait_for_interrupt();
+}
+#endif
-- 
2.18.0.huawei.25


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [RFC PATCH] riscv: add VMAP_STACK overflow detection
  2021-05-07  9:25 ` Tong Tiangen
  (?)
@ 2021-05-07 12:00 ` kernel test robot
  -1 siblings, 0 replies; 9+ messages in thread
From: kernel test robot @ 2021-05-07 12:00 UTC (permalink / raw)
  To: kbuild-all

[-- Attachment #1: Type: text/plain, Size: 3370 bytes --]

Hi Tong,

[FYI, it's a private test report for your RFC patch.]
[auto build test WARNING on v5.12]
[also build test WARNING on next-20210507]
[cannot apply to linus/master]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Tong-Tiangen/riscv-add-VMAP_STACK-overflow-detection/20210507-171736
base:    9f4ad9e425a1d3b6a34617b8ea226d56a119a717
config: riscv-randconfig-r015-20210507 (attached as .config)
compiler: clang version 13.0.0 (https://github.com/llvm/llvm-project a3a8a1a15b524d91b5308db68e9d293b34cd88dd)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # install riscv cross compiling tool for clang build
        # apt-get install binutils-riscv64-linux-gnu
        # https://github.com/0day-ci/linux/commit/60d8a3da002bf59ec0abeb88403dbb11cf31583f
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Tong-Tiangen/riscv-add-VMAP_STACK-overflow-detection/20210507-171736
        git checkout 60d8a3da002bf59ec0abeb88403dbb11cf31583f
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 ARCH=riscv 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> arch/riscv/kernel/traps.c:212:26: warning: no previous prototype for function 'get_overflow_stack' [-Wmissing-prototypes]
   asmlinkage unsigned long get_overflow_stack(void)
                            ^
   arch/riscv/kernel/traps.c:212:12: note: declare 'static' if the function is not intended to be used outside of this translation unit
   asmlinkage unsigned long get_overflow_stack(void)
              ^
              static 
>> arch/riscv/kernel/traps.c:218:17: warning: no previous prototype for function 'handle_bad_stack' [-Wmissing-prototypes]
   asmlinkage void handle_bad_stack(struct pt_regs *regs)
                   ^
   arch/riscv/kernel/traps.c:218:12: note: declare 'static' if the function is not intended to be used outside of this translation unit
   asmlinkage void handle_bad_stack(struct pt_regs *regs)
              ^
              static 
   2 warnings generated.


vim +/get_overflow_stack +212 arch/riscv/kernel/traps.c

   203	
   204	#ifdef CONFIG_VMAP_STACK
   205	DEFINE_PER_CPU(unsigned long [OVERFLOW_STACK_SIZE/sizeof(long)], overflow_stack)
   206			__aligned(16);
   207	/*
   208	 * shadow stack, handled_ kernel_ stack_ overflow(in kernel/entry.S) is used
   209	 * to get per-cpu overflow stack(get_overflow_stack).
   210	 */
   211	long shadow_stack[SHADOW_OVERFLOW_STACK_SIZE/sizeof(long)];
 > 212	asmlinkage unsigned long get_overflow_stack(void)
   213	{
   214		return (unsigned long)this_cpu_ptr(overflow_stack) +
   215			OVERFLOW_STACK_SIZE;
   216	}
   217	
 > 218	asmlinkage void handle_bad_stack(struct pt_regs *regs)

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org

[-- Attachment #2: config.gz --]
[-- Type: application/gzip, Size: 31413 bytes --]

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC PATCH] riscv: add VMAP_STACK overflow detection
  2021-05-07  9:25 ` Tong Tiangen
  (?)
  (?)
@ 2021-05-07 12:37 ` kernel test robot
  -1 siblings, 0 replies; 9+ messages in thread
From: kernel test robot @ 2021-05-07 12:37 UTC (permalink / raw)
  To: kbuild-all

[-- Attachment #1: Type: text/plain, Size: 1811 bytes --]

Hi Tong,

[FYI, it's a private test report for your RFC patch.]
[auto build test ERROR on v5.12]
[also build test ERROR on next-20210507]
[cannot apply to linus/master]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Tong-Tiangen/riscv-add-VMAP_STACK-overflow-detection/20210507-171736
base:    9f4ad9e425a1d3b6a34617b8ea226d56a119a717
config: riscv-nommu_k210_defconfig (attached as .config)
compiler: riscv64-linux-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/60d8a3da002bf59ec0abeb88403dbb11cf31583f
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Tong-Tiangen/riscv-add-VMAP_STACK-overflow-detection/20210507-171736
        git checkout 60d8a3da002bf59ec0abeb88403dbb11cf31583f
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross W=1 ARCH=riscv 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   riscv64-linux-ld: kernel/fork.o: in function `.L59':
>> fork.c:(.text+0x222): undefined reference to `vfree_atomic'
   riscv64-linux-ld: kernel/fork.o: in function `.L283':
>> fork.c:(.text+0xd8a): undefined reference to `find_vm_area'

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org

[-- Attachment #2: config.gz --]
[-- Type: application/gzip, Size: 8401 bytes --]

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC PATCH] riscv: add VMAP_STACK overflow detection
  2021-05-07  9:25 ` Tong Tiangen
                   ` (2 preceding siblings ...)
  (?)
@ 2021-05-07 13:22 ` kernel test robot
  -1 siblings, 0 replies; 9+ messages in thread
From: kernel test robot @ 2021-05-07 13:22 UTC (permalink / raw)
  To: kbuild-all

[-- Attachment #1: Type: text/plain, Size: 5861 bytes --]

Hi Tong,

[FYI, it's a private test report for your RFC patch.]
[auto build test ERROR on v5.12]
[also build test ERROR on next-20210507]
[cannot apply to linus/master]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Tong-Tiangen/riscv-add-VMAP_STACK-overflow-detection/20210507-171736
base:    9f4ad9e425a1d3b6a34617b8ea226d56a119a717
config: riscv-rv32_defconfig (attached as .config)
compiler: riscv32-linux-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/60d8a3da002bf59ec0abeb88403dbb11cf31583f
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Tong-Tiangen/riscv-add-VMAP_STACK-overflow-detection/20210507-171736
        git checkout 60d8a3da002bf59ec0abeb88403dbb11cf31583f
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross W=1 ARCH=riscv 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   arch/riscv/kernel/entry.S: Assembler messages:
>> arch/riscv/kernel/entry.S:416: Error: unrecognized opcode `sd a0,-8(sp)'
>> arch/riscv/kernel/entry.S:436: Error: unrecognized opcode `ld sp,-8(sp)'


vim +416 arch/riscv/kernel/entry.S

   352	
   353	work_pending:
   354		/* Enter slow path for supplementary processing */
   355		la ra, ret_from_exception
   356		andi s1, s0, _TIF_NEED_RESCHED
   357		bnez s1, work_resched
   358	work_notifysig:
   359		/* Handle pending signals and notify-resume requests */
   360		csrs CSR_STATUS, SR_IE /* Enable interrupts for do_notify_resume() */
   361		move a0, sp /* pt_regs */
   362		move a1, s0 /* current_thread_info->flags */
   363		tail do_notify_resume
   364	work_resched:
   365		tail schedule
   366	
   367	/* Slow paths for ptrace. */
   368	handle_syscall_trace_enter:
   369		move a0, sp
   370		call do_syscall_trace_enter
   371		move t0, a0
   372		REG_L a0, PT_A0(sp)
   373		REG_L a1, PT_A1(sp)
   374		REG_L a2, PT_A2(sp)
   375		REG_L a3, PT_A3(sp)
   376		REG_L a4, PT_A4(sp)
   377		REG_L a5, PT_A5(sp)
   378		REG_L a6, PT_A6(sp)
   379		REG_L a7, PT_A7(sp)
   380		bnez t0, ret_from_syscall_rejected
   381		j check_syscall_nr
   382	handle_syscall_trace_exit:
   383		move a0, sp
   384		call do_syscall_trace_exit
   385		j ret_from_exception
   386	
   387	#ifdef CONFIG_VMAP_STACK
   388	handle_kernel_stack_overflow:
   389		la sp, shadow_stack
   390		addi sp, sp, SHADOW_OVERFLOW_STACK_SIZE
   391	
   392		//save caller register to shadow stack
   393		addi sp, sp, -(PT_SIZE_ON_STACK)
   394		REG_S x1,  PT_RA(sp)
   395		REG_S x5,  PT_T0(sp)
   396		REG_S x6,  PT_T1(sp)
   397		REG_S x7,  PT_T2(sp)
   398		REG_S x10, PT_A0(sp)
   399		REG_S x11, PT_A1(sp)
   400		REG_S x12, PT_A2(sp)
   401		REG_S x13, PT_A3(sp)
   402		REG_S x14, PT_A4(sp)
   403		REG_S x15, PT_A5(sp)
   404		REG_S x16, PT_A6(sp)
   405		REG_S x17, PT_A7(sp)
   406		REG_S x28, PT_T3(sp)
   407		REG_S x29, PT_T4(sp)
   408		REG_S x30, PT_T5(sp)
   409		REG_S x31, PT_T6(sp)
   410	
   411		la ra, restore_caller_reg
   412		tail get_overflow_stack
   413	
   414	restore_caller_reg:
   415		//save per-cpu overflow stack
 > 416		sd a0, -8(sp)
   417		//restore caller register from shadow_stack
   418		REG_L x1,  PT_RA(sp)
   419		REG_L x5,  PT_T0(sp)
   420		REG_L x6,  PT_T1(sp)
   421		REG_L x7,  PT_T2(sp)
   422		REG_L x10, PT_A0(sp)
   423		REG_L x11, PT_A1(sp)
   424		REG_L x12, PT_A2(sp)
   425		REG_L x13, PT_A3(sp)
   426		REG_L x14, PT_A4(sp)
   427		REG_L x15, PT_A5(sp)
   428		REG_L x16, PT_A6(sp)
   429		REG_L x17, PT_A7(sp)
   430		REG_L x28, PT_T3(sp)
   431		REG_L x29, PT_T4(sp)
   432		REG_L x30, PT_T5(sp)
   433		REG_L x31, PT_T6(sp)
   434	
   435		//load per-cpu overflow stack
 > 436		ld sp, -8(sp)
   437		addi sp, sp, -(PT_SIZE_ON_STACK)
   438	
   439		//save context to overflow stack
   440		REG_S x1,  PT_RA(sp)
   441		REG_S x3,  PT_GP(sp)
   442		REG_S x5,  PT_T0(sp)
   443		REG_S x6,  PT_T1(sp)
   444		REG_S x7,  PT_T2(sp)
   445		REG_S x8,  PT_S0(sp)
   446		REG_S x9,  PT_S1(sp)
   447		REG_S x10, PT_A0(sp)
   448		REG_S x11, PT_A1(sp)
   449		REG_S x12, PT_A2(sp)
   450		REG_S x13, PT_A3(sp)
   451		REG_S x14, PT_A4(sp)
   452		REG_S x15, PT_A5(sp)
   453		REG_S x16, PT_A6(sp)
   454		REG_S x17, PT_A7(sp)
   455		REG_S x18, PT_S2(sp)
   456		REG_S x19, PT_S3(sp)
   457		REG_S x20, PT_S4(sp)
   458		REG_S x21, PT_S5(sp)
   459		REG_S x22, PT_S6(sp)
   460		REG_S x23, PT_S7(sp)
   461		REG_S x24, PT_S8(sp)
   462		REG_S x25, PT_S9(sp)
   463		REG_S x26, PT_S10(sp)
   464		REG_S x27, PT_S11(sp)
   465		REG_S x28, PT_T3(sp)
   466		REG_S x29, PT_T4(sp)
   467		REG_S x30, PT_T5(sp)
   468		REG_S x31, PT_T6(sp)
   469	
   470		REG_L s0, TASK_TI_KERNEL_SP(tp)
   471		csrr s1, CSR_STATUS
   472		csrr s2, CSR_EPC
   473		csrr s3, CSR_TVAL
   474		csrr s4, CSR_CAUSE
   475		csrr s5, CSR_SCRATCH
   476		REG_S s0, PT_SP(sp)
   477		REG_S s1, PT_STATUS(sp)
   478		REG_S s2, PT_EPC(sp)
   479		REG_S s3, PT_BADADDR(sp)
   480		REG_S s4, PT_CAUSE(sp)
   481		REG_S s5, PT_TP(sp)
   482		move a0, sp
   483		tail handle_bad_stack
   484	#endif
   485	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org

[-- Attachment #2: config.gz --]
[-- Type: application/gzip, Size: 20565 bytes --]

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC PATCH] riscv: add VMAP_STACK overflow detection
  2021-05-07  9:25 ` Tong Tiangen
@ 2021-05-23  1:49   ` Palmer Dabbelt
  -1 siblings, 0 replies; 9+ messages in thread
From: Palmer Dabbelt @ 2021-05-23  1:49 UTC (permalink / raw)
  To: tongtiangen; +Cc: Paul Walmsley, aou, linux-riscv, linux-kernel, tongtiangen

On Fri, 07 May 2021 02:25:09 PDT (-0700), tongtiangen@huawei.com wrote:
> This patch adds stack overflow detection to riscv, usable when
> CONFIG_VMAP_STACK=y.
>
> Overflow is detected in kernel exception entry(kernel/entry.S), if the kernel
> stack is overflow and been detected, the overflow handler is invoked on a
> per-cpu overflow stack. This approach preserves GPRs and the original exception
> information.
>
> The overflow detect is performed before any attempt is made to access the stack
> and the principle of stack overflow detection: kernel stacks are aligned to
> double their size, enabling overflow to be detected with a single bit test. For
> example, a 16K stack is aligned to 32K, ensuring that bit 14 of the SP must be
> zero. On an overflow (or underflow), this bit is flipped. Thus, overflow (of
> less than the size of the stack) can be detected by testing whether this bit is
> set.
>
> This gives us a useful error message on stack overflow, as can be trigger with
> the LKDTM overflow test:
>
> [  388.053267] lkdtm: Performing direct entry EXHAUST_STACK
> [  388.053663] lkdtm: Calling function with 1024 frame size to depth 32 ...
> [  388.054016] lkdtm: loop 32/32 ...
> [  388.054186] lkdtm: loop 31/32 ...
> [  388.054491] lkdtm: loop 30/32 ...
> [  388.054672] lkdtm: loop 29/32 ...
> [  388.054859] lkdtm: loop 28/32 ...
> [  388.055010] lkdtm: loop 27/32 ...
> [  388.055163] lkdtm: loop 26/32 ...
> [  388.055309] lkdtm: loop 25/32 ...
> [  388.055481] lkdtm: loop 24/32 ...
> [  388.055653] lkdtm: loop 23/32 ...
> [  388.055837] lkdtm: loop 22/32 ...
> [  388.056015] lkdtm: loop 21/32 ...
> [  388.056188] lkdtm: loop 20/32 ...
> [  388.058145] Insufficient stack space to handle exception!
> [  388.058153] Task stack:     [0xffffffd014260000..0xffffffd014264000]
> [  388.058160] Overflow stack: [0xffffffe1f8d2c220..0xffffffe1f8d2d220]
> [  388.058168] CPU: 0 PID: 89 Comm: bash Not tainted 5.12.0-rc8-dirty #90
> [  388.058175] Hardware name: riscv-virtio,qemu (DT)
> [  388.058187] epc : number+0x32/0x2c0
> [  388.058247]  ra : vsnprintf+0x2ae/0x3f0
> [  388.058255] epc : ffffffe0002d38f6 ra : ffffffe0002d814e sp : ffffffd01425ffc0
> [  388.058263]  gp : ffffffe0012e4010 tp : ffffffe08014da00 t0 : ffffffd0142606e8
> [  388.058271]  t1 : 0000000000000000 t2 : 0000000000000000 s0 : ffffffd014260070
> [  388.058303]  s1 : ffffffd014260158 a0 : ffffffd01426015e a1 : ffffffd014260158
> [  388.058311]  a2 : 0000000000000013 a3 : ffff0a01ffffff10 a4 : ffffffe000c398e0
> [  388.058319]  a5 : 511b02ec65f3e300 a6 : 0000000000a1749a a7 : 0000000000000000
> [  388.058327]  s2 : ffffffff000000ff s3 : 00000000ffff0a01 s4 : ffffffe0012e50a8
> [  388.058335]  s5 : 0000000000ffff0a s6 : ffffffe0012e50a8 s7 : ffffffe000da1cc0
> [  388.058343]  s8 : ffffffffffffffff s9 : ffffffd0142602b0 s10: ffffffd0142602a8
> [  388.058351]  s11: ffffffd01426015e t3 : 00000000000f0000 t4 : ffffffffffffffff
> [  388.058359]  t5 : 000000000000002f t6 : ffffffd014260158
> [  388.058366] status: 0000000000000100 badaddr: ffffffd01425fff8 cause: 000000000000000f
> [  388.058374] Kernel panic - not syncing: Kernel stack overflow
> [  388.058381] CPU: 0 PID: 89 Comm: bash Not tainted 5.12.0-rc8-dirty #90
> [  388.058387] Hardware name: riscv-virtio,qemu (DT)
> [  388.058393] Call Trace:
> [  388.058400] [<ffffffe000004944>] walk_stackframe+0x0/0xce
> [  388.058406] [<ffffffe0006f0b28>] dump_backtrace+0x38/0x46
> [  388.058412] [<ffffffe0006f0b46>] show_stack+0x10/0x18
> [  388.058418] [<ffffffe0006f3690>] dump_stack+0x74/0x8e
> [  388.058424] [<ffffffe0006f0d52>] panic+0xfc/0x2b2
> [  388.058430] [<ffffffe0006f0acc>] print_trace_address+0x0/0x24
> [  388.058436] [<ffffffe0002d814e>] vsnprintf+0x2ae/0x3f0
> [  388.058956] SMP: stopping secondary CPUs
>
> Signed-off-by: Tong Tiangen <tongtiangen@huawei.com>
> ---
>  arch/riscv/Kconfig                   |   1 +
>  arch/riscv/include/asm/thread_info.h |  15 ++++
>  arch/riscv/kernel/entry.S            | 108 +++++++++++++++++++++++++++
>  arch/riscv/kernel/traps.c            |  35 +++++++++
>  4 files changed, 159 insertions(+)
>
> diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
> index 4515a10c5d22..587f001e84f4 100644
> --- a/arch/riscv/Kconfig
> +++ b/arch/riscv/Kconfig
> @@ -63,6 +63,7 @@ config RISCV
>  	select HAVE_ARCH_MMAP_RND_BITS if MMU
>  	select HAVE_ARCH_SECCOMP_FILTER
>  	select HAVE_ARCH_TRACEHOOK
> +	select HAVE_ARCH_VMAP_STACK
>  	select HAVE_ASM_MODVERSIONS
>  	select HAVE_CONTEXT_TRACKING
>  	select HAVE_DEBUG_KMEMLEAK
> diff --git a/arch/riscv/include/asm/thread_info.h b/arch/riscv/include/asm/thread_info.h
> index 0e549a3089b3..60da0dcacf14 100644
> --- a/arch/riscv/include/asm/thread_info.h
> +++ b/arch/riscv/include/asm/thread_info.h
> @@ -19,6 +19,21 @@
>  #endif
>  #define THREAD_SIZE		(PAGE_SIZE << THREAD_SIZE_ORDER)
>
> +/*
> + * By aligning VMAP'd stacks to 2 * THREAD_SIZE, we can detect overflow by
> + * checking sp & (1 << THREAD_SHIFT), which we can do cheaply in the entry
> + * assembly.
> + */
> +#ifdef CONFIG_VMAP_STACK
> +#define THREAD_ALIGN            (2 * THREAD_SIZE)
> +#else
> +#define THREAD_ALIGN            THREAD_SIZE
> +#endif
> +
> +#define THREAD_SHIFT            (PAGE_SHIFT + THREAD_SIZE_ORDER)
> +#define OVERFLOW_STACK_SIZE     SZ_4K
> +#define SHADOW_OVERFLOW_STACK_SIZE (1024)
> +
>  #ifndef __ASSEMBLY__
>
>  #include <asm/processor.h>
> diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S
> index 83095faa680e..deadf4000b86 100644
> --- a/arch/riscv/kernel/entry.S
> +++ b/arch/riscv/kernel/entry.S
> @@ -29,6 +29,15 @@ ENTRY(handle_exception)
>  _restore_kernel_tpsp:
>  	csrr tp, CSR_SCRATCH
>  	REG_S sp, TASK_TI_KERNEL_SP(tp)
> +
> +#ifdef CONFIG_VMAP_STACK
> +	addi sp, sp, -(PT_SIZE_ON_STACK)
> +	srli sp, sp, THREAD_SHIFT
> +	andi sp, sp, 0x1
> +	bnez sp, handle_kernel_stack_overflow
> +	REG_L sp, TASK_TI_KERNEL_SP(tp)
> +#endif
> +
>  _save_context:
>  	REG_S sp, TASK_TI_USER_SP(tp)
>  	REG_L sp, TASK_TI_KERNEL_SP(tp)
> @@ -375,6 +384,105 @@ handle_syscall_trace_exit:
>  	call do_syscall_trace_exit
>  	j ret_from_exception
>
> +#ifdef CONFIG_VMAP_STACK
> +handle_kernel_stack_overflow:
> +	la sp, shadow_stack
> +	addi sp, sp, SHADOW_OVERFLOW_STACK_SIZE
> +
> +	//save caller register to shadow stack
> +	addi sp, sp, -(PT_SIZE_ON_STACK)
> +	REG_S x1,  PT_RA(sp)
> +	REG_S x5,  PT_T0(sp)
> +	REG_S x6,  PT_T1(sp)
> +	REG_S x7,  PT_T2(sp)
> +	REG_S x10, PT_A0(sp)
> +	REG_S x11, PT_A1(sp)
> +	REG_S x12, PT_A2(sp)
> +	REG_S x13, PT_A3(sp)
> +	REG_S x14, PT_A4(sp)
> +	REG_S x15, PT_A5(sp)
> +	REG_S x16, PT_A6(sp)
> +	REG_S x17, PT_A7(sp)
> +	REG_S x28, PT_T3(sp)
> +	REG_S x29, PT_T4(sp)
> +	REG_S x30, PT_T5(sp)
> +	REG_S x31, PT_T6(sp)
> +
> +	la ra, restore_caller_reg
> +	tail get_overflow_stack
> +
> +restore_caller_reg:
> +	//save per-cpu overflow stack
> +	sd a0, -8(sp)
> +	//restore caller register from shadow_stack
> +	REG_L x1,  PT_RA(sp)
> +	REG_L x5,  PT_T0(sp)
> +	REG_L x6,  PT_T1(sp)
> +	REG_L x7,  PT_T2(sp)
> +	REG_L x10, PT_A0(sp)
> +	REG_L x11, PT_A1(sp)
> +	REG_L x12, PT_A2(sp)
> +	REG_L x13, PT_A3(sp)
> +	REG_L x14, PT_A4(sp)
> +	REG_L x15, PT_A5(sp)
> +	REG_L x16, PT_A6(sp)
> +	REG_L x17, PT_A7(sp)
> +	REG_L x28, PT_T3(sp)
> +	REG_L x29, PT_T4(sp)
> +	REG_L x30, PT_T5(sp)
> +	REG_L x31, PT_T6(sp)
> +
> +	//load per-cpu overflow stack
> +	ld sp, -8(sp)
> +	addi sp, sp, -(PT_SIZE_ON_STACK)
> +
> +	//save context to overflow stack
> +	REG_S x1,  PT_RA(sp)
> +	REG_S x3,  PT_GP(sp)
> +	REG_S x5,  PT_T0(sp)
> +	REG_S x6,  PT_T1(sp)
> +	REG_S x7,  PT_T2(sp)
> +	REG_S x8,  PT_S0(sp)
> +	REG_S x9,  PT_S1(sp)
> +	REG_S x10, PT_A0(sp)
> +	REG_S x11, PT_A1(sp)
> +	REG_S x12, PT_A2(sp)
> +	REG_S x13, PT_A3(sp)
> +	REG_S x14, PT_A4(sp)
> +	REG_S x15, PT_A5(sp)
> +	REG_S x16, PT_A6(sp)
> +	REG_S x17, PT_A7(sp)
> +	REG_S x18, PT_S2(sp)
> +	REG_S x19, PT_S3(sp)
> +	REG_S x20, PT_S4(sp)
> +	REG_S x21, PT_S5(sp)
> +	REG_S x22, PT_S6(sp)
> +	REG_S x23, PT_S7(sp)
> +	REG_S x24, PT_S8(sp)
> +	REG_S x25, PT_S9(sp)
> +	REG_S x26, PT_S10(sp)
> +	REG_S x27, PT_S11(sp)
> +	REG_S x28, PT_T3(sp)
> +	REG_S x29, PT_T4(sp)
> +	REG_S x30, PT_T5(sp)
> +	REG_S x31, PT_T6(sp)
> +
> +	REG_L s0, TASK_TI_KERNEL_SP(tp)
> +	csrr s1, CSR_STATUS
> +	csrr s2, CSR_EPC
> +	csrr s3, CSR_TVAL
> +	csrr s4, CSR_CAUSE
> +	csrr s5, CSR_SCRATCH
> +	REG_S s0, PT_SP(sp)
> +	REG_S s1, PT_STATUS(sp)
> +	REG_S s2, PT_EPC(sp)
> +	REG_S s3, PT_BADADDR(sp)
> +	REG_S s4, PT_CAUSE(sp)
> +	REG_S s5, PT_TP(sp)
> +	move a0, sp
> +	tail handle_bad_stack
> +#endif
> +
>  END(handle_exception)
>
>  ENTRY(ret_from_fork)
> diff --git a/arch/riscv/kernel/traps.c b/arch/riscv/kernel/traps.c
> index 1357abf79570..ff8033b9fdd6 100644
> --- a/arch/riscv/kernel/traps.c
> +++ b/arch/riscv/kernel/traps.c
> @@ -200,3 +200,38 @@ int is_valid_bugaddr(unsigned long pc)
>  void trap_init(void)
>  {
>  }
> +
> +#ifdef CONFIG_VMAP_STACK
> +DEFINE_PER_CPU(unsigned long [OVERFLOW_STACK_SIZE/sizeof(long)], overflow_stack)
> +		__aligned(16);
> +/*
> + * shadow stack, handled_ kernel_ stack_ overflow(in kernel/entry.S) is used
> + * to get per-cpu overflow stack(get_overflow_stack).
> + */
> +long shadow_stack[SHADOW_OVERFLOW_STACK_SIZE/sizeof(long)];
> +asmlinkage unsigned long get_overflow_stack(void)
> +{
> +	return (unsigned long)this_cpu_ptr(overflow_stack) +
> +		OVERFLOW_STACK_SIZE;
> +}
> +
> +asmlinkage void handle_bad_stack(struct pt_regs *regs)
> +{
> +	unsigned long tsk_stk = (unsigned long)current->stack;
> +	unsigned long ovf_stk = (unsigned long)this_cpu_ptr(overflow_stack);
> +
> +	console_verbose();
> +
> +	pr_emerg("Insufficient stack space to handle exception!\n");
> +	pr_emerg("Task stack:     [0x%016lx..0x%016lx]\n",
> +			tsk_stk, tsk_stk + THREAD_SIZE);
> +	pr_emerg("Overflow stack: [0x%016lx..0x%016lx]\n",
> +			ovf_stk, ovf_stk + OVERFLOW_STACK_SIZE);
> +
> +	__show_regs(regs);
> +	panic("Kernel stack overflow");
> +
> +	for (;;)
> +		wait_for_interrupt();
> +}
> +#endif

This LGTM.  It seems good enough to take, I'm not sure why this is an 
RFC?

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC PATCH] riscv: add VMAP_STACK overflow detection
@ 2021-05-23  1:49   ` Palmer Dabbelt
  0 siblings, 0 replies; 9+ messages in thread
From: Palmer Dabbelt @ 2021-05-23  1:49 UTC (permalink / raw)
  To: tongtiangen; +Cc: Paul Walmsley, aou, linux-riscv, linux-kernel, tongtiangen

On Fri, 07 May 2021 02:25:09 PDT (-0700), tongtiangen@huawei.com wrote:
> This patch adds stack overflow detection to riscv, usable when
> CONFIG_VMAP_STACK=y.
>
> Overflow is detected in kernel exception entry(kernel/entry.S), if the kernel
> stack is overflow and been detected, the overflow handler is invoked on a
> per-cpu overflow stack. This approach preserves GPRs and the original exception
> information.
>
> The overflow detect is performed before any attempt is made to access the stack
> and the principle of stack overflow detection: kernel stacks are aligned to
> double their size, enabling overflow to be detected with a single bit test. For
> example, a 16K stack is aligned to 32K, ensuring that bit 14 of the SP must be
> zero. On an overflow (or underflow), this bit is flipped. Thus, overflow (of
> less than the size of the stack) can be detected by testing whether this bit is
> set.
>
> This gives us a useful error message on stack overflow, as can be trigger with
> the LKDTM overflow test:
>
> [  388.053267] lkdtm: Performing direct entry EXHAUST_STACK
> [  388.053663] lkdtm: Calling function with 1024 frame size to depth 32 ...
> [  388.054016] lkdtm: loop 32/32 ...
> [  388.054186] lkdtm: loop 31/32 ...
> [  388.054491] lkdtm: loop 30/32 ...
> [  388.054672] lkdtm: loop 29/32 ...
> [  388.054859] lkdtm: loop 28/32 ...
> [  388.055010] lkdtm: loop 27/32 ...
> [  388.055163] lkdtm: loop 26/32 ...
> [  388.055309] lkdtm: loop 25/32 ...
> [  388.055481] lkdtm: loop 24/32 ...
> [  388.055653] lkdtm: loop 23/32 ...
> [  388.055837] lkdtm: loop 22/32 ...
> [  388.056015] lkdtm: loop 21/32 ...
> [  388.056188] lkdtm: loop 20/32 ...
> [  388.058145] Insufficient stack space to handle exception!
> [  388.058153] Task stack:     [0xffffffd014260000..0xffffffd014264000]
> [  388.058160] Overflow stack: [0xffffffe1f8d2c220..0xffffffe1f8d2d220]
> [  388.058168] CPU: 0 PID: 89 Comm: bash Not tainted 5.12.0-rc8-dirty #90
> [  388.058175] Hardware name: riscv-virtio,qemu (DT)
> [  388.058187] epc : number+0x32/0x2c0
> [  388.058247]  ra : vsnprintf+0x2ae/0x3f0
> [  388.058255] epc : ffffffe0002d38f6 ra : ffffffe0002d814e sp : ffffffd01425ffc0
> [  388.058263]  gp : ffffffe0012e4010 tp : ffffffe08014da00 t0 : ffffffd0142606e8
> [  388.058271]  t1 : 0000000000000000 t2 : 0000000000000000 s0 : ffffffd014260070
> [  388.058303]  s1 : ffffffd014260158 a0 : ffffffd01426015e a1 : ffffffd014260158
> [  388.058311]  a2 : 0000000000000013 a3 : ffff0a01ffffff10 a4 : ffffffe000c398e0
> [  388.058319]  a5 : 511b02ec65f3e300 a6 : 0000000000a1749a a7 : 0000000000000000
> [  388.058327]  s2 : ffffffff000000ff s3 : 00000000ffff0a01 s4 : ffffffe0012e50a8
> [  388.058335]  s5 : 0000000000ffff0a s6 : ffffffe0012e50a8 s7 : ffffffe000da1cc0
> [  388.058343]  s8 : ffffffffffffffff s9 : ffffffd0142602b0 s10: ffffffd0142602a8
> [  388.058351]  s11: ffffffd01426015e t3 : 00000000000f0000 t4 : ffffffffffffffff
> [  388.058359]  t5 : 000000000000002f t6 : ffffffd014260158
> [  388.058366] status: 0000000000000100 badaddr: ffffffd01425fff8 cause: 000000000000000f
> [  388.058374] Kernel panic - not syncing: Kernel stack overflow
> [  388.058381] CPU: 0 PID: 89 Comm: bash Not tainted 5.12.0-rc8-dirty #90
> [  388.058387] Hardware name: riscv-virtio,qemu (DT)
> [  388.058393] Call Trace:
> [  388.058400] [<ffffffe000004944>] walk_stackframe+0x0/0xce
> [  388.058406] [<ffffffe0006f0b28>] dump_backtrace+0x38/0x46
> [  388.058412] [<ffffffe0006f0b46>] show_stack+0x10/0x18
> [  388.058418] [<ffffffe0006f3690>] dump_stack+0x74/0x8e
> [  388.058424] [<ffffffe0006f0d52>] panic+0xfc/0x2b2
> [  388.058430] [<ffffffe0006f0acc>] print_trace_address+0x0/0x24
> [  388.058436] [<ffffffe0002d814e>] vsnprintf+0x2ae/0x3f0
> [  388.058956] SMP: stopping secondary CPUs
>
> Signed-off-by: Tong Tiangen <tongtiangen@huawei.com>
> ---
>  arch/riscv/Kconfig                   |   1 +
>  arch/riscv/include/asm/thread_info.h |  15 ++++
>  arch/riscv/kernel/entry.S            | 108 +++++++++++++++++++++++++++
>  arch/riscv/kernel/traps.c            |  35 +++++++++
>  4 files changed, 159 insertions(+)
>
> diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
> index 4515a10c5d22..587f001e84f4 100644
> --- a/arch/riscv/Kconfig
> +++ b/arch/riscv/Kconfig
> @@ -63,6 +63,7 @@ config RISCV
>  	select HAVE_ARCH_MMAP_RND_BITS if MMU
>  	select HAVE_ARCH_SECCOMP_FILTER
>  	select HAVE_ARCH_TRACEHOOK
> +	select HAVE_ARCH_VMAP_STACK
>  	select HAVE_ASM_MODVERSIONS
>  	select HAVE_CONTEXT_TRACKING
>  	select HAVE_DEBUG_KMEMLEAK
> diff --git a/arch/riscv/include/asm/thread_info.h b/arch/riscv/include/asm/thread_info.h
> index 0e549a3089b3..60da0dcacf14 100644
> --- a/arch/riscv/include/asm/thread_info.h
> +++ b/arch/riscv/include/asm/thread_info.h
> @@ -19,6 +19,21 @@
>  #endif
>  #define THREAD_SIZE		(PAGE_SIZE << THREAD_SIZE_ORDER)
>
> +/*
> + * By aligning VMAP'd stacks to 2 * THREAD_SIZE, we can detect overflow by
> + * checking sp & (1 << THREAD_SHIFT), which we can do cheaply in the entry
> + * assembly.
> + */
> +#ifdef CONFIG_VMAP_STACK
> +#define THREAD_ALIGN            (2 * THREAD_SIZE)
> +#else
> +#define THREAD_ALIGN            THREAD_SIZE
> +#endif
> +
> +#define THREAD_SHIFT            (PAGE_SHIFT + THREAD_SIZE_ORDER)
> +#define OVERFLOW_STACK_SIZE     SZ_4K
> +#define SHADOW_OVERFLOW_STACK_SIZE (1024)
> +
>  #ifndef __ASSEMBLY__
>
>  #include <asm/processor.h>
> diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S
> index 83095faa680e..deadf4000b86 100644
> --- a/arch/riscv/kernel/entry.S
> +++ b/arch/riscv/kernel/entry.S
> @@ -29,6 +29,15 @@ ENTRY(handle_exception)
>  _restore_kernel_tpsp:
>  	csrr tp, CSR_SCRATCH
>  	REG_S sp, TASK_TI_KERNEL_SP(tp)
> +
> +#ifdef CONFIG_VMAP_STACK
> +	addi sp, sp, -(PT_SIZE_ON_STACK)
> +	srli sp, sp, THREAD_SHIFT
> +	andi sp, sp, 0x1
> +	bnez sp, handle_kernel_stack_overflow
> +	REG_L sp, TASK_TI_KERNEL_SP(tp)
> +#endif
> +
>  _save_context:
>  	REG_S sp, TASK_TI_USER_SP(tp)
>  	REG_L sp, TASK_TI_KERNEL_SP(tp)
> @@ -375,6 +384,105 @@ handle_syscall_trace_exit:
>  	call do_syscall_trace_exit
>  	j ret_from_exception
>
> +#ifdef CONFIG_VMAP_STACK
> +handle_kernel_stack_overflow:
> +	la sp, shadow_stack
> +	addi sp, sp, SHADOW_OVERFLOW_STACK_SIZE
> +
> +	//save caller register to shadow stack
> +	addi sp, sp, -(PT_SIZE_ON_STACK)
> +	REG_S x1,  PT_RA(sp)
> +	REG_S x5,  PT_T0(sp)
> +	REG_S x6,  PT_T1(sp)
> +	REG_S x7,  PT_T2(sp)
> +	REG_S x10, PT_A0(sp)
> +	REG_S x11, PT_A1(sp)
> +	REG_S x12, PT_A2(sp)
> +	REG_S x13, PT_A3(sp)
> +	REG_S x14, PT_A4(sp)
> +	REG_S x15, PT_A5(sp)
> +	REG_S x16, PT_A6(sp)
> +	REG_S x17, PT_A7(sp)
> +	REG_S x28, PT_T3(sp)
> +	REG_S x29, PT_T4(sp)
> +	REG_S x30, PT_T5(sp)
> +	REG_S x31, PT_T6(sp)
> +
> +	la ra, restore_caller_reg
> +	tail get_overflow_stack
> +
> +restore_caller_reg:
> +	//save per-cpu overflow stack
> +	sd a0, -8(sp)
> +	//restore caller register from shadow_stack
> +	REG_L x1,  PT_RA(sp)
> +	REG_L x5,  PT_T0(sp)
> +	REG_L x6,  PT_T1(sp)
> +	REG_L x7,  PT_T2(sp)
> +	REG_L x10, PT_A0(sp)
> +	REG_L x11, PT_A1(sp)
> +	REG_L x12, PT_A2(sp)
> +	REG_L x13, PT_A3(sp)
> +	REG_L x14, PT_A4(sp)
> +	REG_L x15, PT_A5(sp)
> +	REG_L x16, PT_A6(sp)
> +	REG_L x17, PT_A7(sp)
> +	REG_L x28, PT_T3(sp)
> +	REG_L x29, PT_T4(sp)
> +	REG_L x30, PT_T5(sp)
> +	REG_L x31, PT_T6(sp)
> +
> +	//load per-cpu overflow stack
> +	ld sp, -8(sp)
> +	addi sp, sp, -(PT_SIZE_ON_STACK)
> +
> +	//save context to overflow stack
> +	REG_S x1,  PT_RA(sp)
> +	REG_S x3,  PT_GP(sp)
> +	REG_S x5,  PT_T0(sp)
> +	REG_S x6,  PT_T1(sp)
> +	REG_S x7,  PT_T2(sp)
> +	REG_S x8,  PT_S0(sp)
> +	REG_S x9,  PT_S1(sp)
> +	REG_S x10, PT_A0(sp)
> +	REG_S x11, PT_A1(sp)
> +	REG_S x12, PT_A2(sp)
> +	REG_S x13, PT_A3(sp)
> +	REG_S x14, PT_A4(sp)
> +	REG_S x15, PT_A5(sp)
> +	REG_S x16, PT_A6(sp)
> +	REG_S x17, PT_A7(sp)
> +	REG_S x18, PT_S2(sp)
> +	REG_S x19, PT_S3(sp)
> +	REG_S x20, PT_S4(sp)
> +	REG_S x21, PT_S5(sp)
> +	REG_S x22, PT_S6(sp)
> +	REG_S x23, PT_S7(sp)
> +	REG_S x24, PT_S8(sp)
> +	REG_S x25, PT_S9(sp)
> +	REG_S x26, PT_S10(sp)
> +	REG_S x27, PT_S11(sp)
> +	REG_S x28, PT_T3(sp)
> +	REG_S x29, PT_T4(sp)
> +	REG_S x30, PT_T5(sp)
> +	REG_S x31, PT_T6(sp)
> +
> +	REG_L s0, TASK_TI_KERNEL_SP(tp)
> +	csrr s1, CSR_STATUS
> +	csrr s2, CSR_EPC
> +	csrr s3, CSR_TVAL
> +	csrr s4, CSR_CAUSE
> +	csrr s5, CSR_SCRATCH
> +	REG_S s0, PT_SP(sp)
> +	REG_S s1, PT_STATUS(sp)
> +	REG_S s2, PT_EPC(sp)
> +	REG_S s3, PT_BADADDR(sp)
> +	REG_S s4, PT_CAUSE(sp)
> +	REG_S s5, PT_TP(sp)
> +	move a0, sp
> +	tail handle_bad_stack
> +#endif
> +
>  END(handle_exception)
>
>  ENTRY(ret_from_fork)
> diff --git a/arch/riscv/kernel/traps.c b/arch/riscv/kernel/traps.c
> index 1357abf79570..ff8033b9fdd6 100644
> --- a/arch/riscv/kernel/traps.c
> +++ b/arch/riscv/kernel/traps.c
> @@ -200,3 +200,38 @@ int is_valid_bugaddr(unsigned long pc)
>  void trap_init(void)
>  {
>  }
> +
> +#ifdef CONFIG_VMAP_STACK
> +DEFINE_PER_CPU(unsigned long [OVERFLOW_STACK_SIZE/sizeof(long)], overflow_stack)
> +		__aligned(16);
> +/*
> + * shadow stack, handled_ kernel_ stack_ overflow(in kernel/entry.S) is used
> + * to get per-cpu overflow stack(get_overflow_stack).
> + */
> +long shadow_stack[SHADOW_OVERFLOW_STACK_SIZE/sizeof(long)];
> +asmlinkage unsigned long get_overflow_stack(void)
> +{
> +	return (unsigned long)this_cpu_ptr(overflow_stack) +
> +		OVERFLOW_STACK_SIZE;
> +}
> +
> +asmlinkage void handle_bad_stack(struct pt_regs *regs)
> +{
> +	unsigned long tsk_stk = (unsigned long)current->stack;
> +	unsigned long ovf_stk = (unsigned long)this_cpu_ptr(overflow_stack);
> +
> +	console_verbose();
> +
> +	pr_emerg("Insufficient stack space to handle exception!\n");
> +	pr_emerg("Task stack:     [0x%016lx..0x%016lx]\n",
> +			tsk_stk, tsk_stk + THREAD_SIZE);
> +	pr_emerg("Overflow stack: [0x%016lx..0x%016lx]\n",
> +			ovf_stk, ovf_stk + OVERFLOW_STACK_SIZE);
> +
> +	__show_regs(regs);
> +	panic("Kernel stack overflow");
> +
> +	for (;;)
> +		wait_for_interrupt();
> +}
> +#endif

This LGTM.  It seems good enough to take, I'm not sure why this is an 
RFC?

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC PATCH] riscv: add VMAP_STACK overflow detection
  2021-05-23  1:49   ` Palmer Dabbelt
@ 2021-05-24 13:51     ` tongtiangen
  -1 siblings, 0 replies; 9+ messages in thread
From: tongtiangen @ 2021-05-24 13:51 UTC (permalink / raw)
  To: Palmer Dabbelt; +Cc: Paul Walmsley, aou, linux-riscv, linux-kernel



On 2021/5/23 9:49, Palmer Dabbelt wrote:
> On Fri, 07 May 2021 02:25:09 PDT (-0700), tongtiangen@huawei.com wrote:
>> This patch adds stack overflow detection to riscv, usable when
>> CONFIG_VMAP_STACK=y.
>>
>> Overflow is detected in kernel exception entry(kernel/entry.S), if 
>> the kernel
>> stack is overflow and been detected, the overflow handler is invoked 
>> on a
>> per-cpu overflow stack. This approach preserves GPRs and the original 
>> exception
>> information.
>>
>> The overflow detect is performed before any attempt is made to access 
>> the stack
>> and the principle of stack overflow detection: kernel stacks are 
>> aligned to
>> double their size, enabling overflow to be detected with a single bit 
>> test. For
>> example, a 16K stack is aligned to 32K, ensuring that bit 14 of the 
>> SP must be
>> zero. On an overflow (or underflow), this bit is flipped. Thus, 
>> overflow (of
>> less than the size of the stack) can be detected by testing whether 
>> this bit is
>> set.
>>
>> This gives us a useful error message on stack overflow, as can be 
>> trigger with
>> the LKDTM overflow test:
>>
>> [  388.053267] lkdtm: Performing direct entry EXHAUST_STACK
>> [  388.053663] lkdtm: Calling function with 1024 frame size to depth 
>> 32 ...
>> [  388.054016] lkdtm: loop 32/32 ...
>> [  388.054186] lkdtm: loop 31/32 ...
>> [  388.054491] lkdtm: loop 30/32 ...
>> [  388.054672] lkdtm: loop 29/32 ...
>> [  388.054859] lkdtm: loop 28/32 ...
>> [  388.055010] lkdtm: loop 27/32 ...
>> [  388.055163] lkdtm: loop 26/32 ...
>> [  388.055309] lkdtm: loop 25/32 ...
>> [  388.055481] lkdtm: loop 24/32 ...
>> [  388.055653] lkdtm: loop 23/32 ...
>> [  388.055837] lkdtm: loop 22/32 ...
>> [  388.056015] lkdtm: loop 21/32 ...
>> [  388.056188] lkdtm: loop 20/32 ...
>> [  388.058145] Insufficient stack space to handle exception!
>> [  388.058153] Task stack: [0xffffffd014260000..0xffffffd014264000]
>> [  388.058160] Overflow stack: [0xffffffe1f8d2c220..0xffffffe1f8d2d220]
>> [  388.058168] CPU: 0 PID: 89 Comm: bash Not tainted 5.12.0-rc8-dirty 
>> #90
>> [  388.058175] Hardware name: riscv-virtio,qemu (DT)
>> [  388.058187] epc : number+0x32/0x2c0
>> [  388.058247]  ra : vsnprintf+0x2ae/0x3f0
>> [  388.058255] epc : ffffffe0002d38f6 ra : ffffffe0002d814e sp : 
>> ffffffd01425ffc0
>> [  388.058263]  gp : ffffffe0012e4010 tp : ffffffe08014da00 t0 : 
>> ffffffd0142606e8
>> [  388.058271]  t1 : 0000000000000000 t2 : 0000000000000000 s0 : 
>> ffffffd014260070
>> [  388.058303]  s1 : ffffffd014260158 a0 : ffffffd01426015e a1 : 
>> ffffffd014260158
>> [  388.058311]  a2 : 0000000000000013 a3 : ffff0a01ffffff10 a4 : 
>> ffffffe000c398e0
>> [  388.058319]  a5 : 511b02ec65f3e300 a6 : 0000000000a1749a a7 : 
>> 0000000000000000
>> [  388.058327]  s2 : ffffffff000000ff s3 : 00000000ffff0a01 s4 : 
>> ffffffe0012e50a8
>> [  388.058335]  s5 : 0000000000ffff0a s6 : ffffffe0012e50a8 s7 : 
>> ffffffe000da1cc0
>> [  388.058343]  s8 : ffffffffffffffff s9 : ffffffd0142602b0 s10: 
>> ffffffd0142602a8
>> [  388.058351]  s11: ffffffd01426015e t3 : 00000000000f0000 t4 : 
>> ffffffffffffffff
>> [  388.058359]  t5 : 000000000000002f t6 : ffffffd014260158
>> [  388.058366] status: 0000000000000100 badaddr: ffffffd01425fff8 
>> cause: 000000000000000f
>> [  388.058374] Kernel panic - not syncing: Kernel stack overflow
>> [  388.058381] CPU: 0 PID: 89 Comm: bash Not tainted 5.12.0-rc8-dirty 
>> #90
>> [  388.058387] Hardware name: riscv-virtio,qemu (DT)
>> [  388.058393] Call Trace:
>> [  388.058400] [<ffffffe000004944>] walk_stackframe+0x0/0xce
>> [  388.058406] [<ffffffe0006f0b28>] dump_backtrace+0x38/0x46
>> [  388.058412] [<ffffffe0006f0b46>] show_stack+0x10/0x18
>> [  388.058418] [<ffffffe0006f3690>] dump_stack+0x74/0x8e
>> [  388.058424] [<ffffffe0006f0d52>] panic+0xfc/0x2b2
>> [  388.058430] [<ffffffe0006f0acc>] print_trace_address+0x0/0x24
>> [  388.058436] [<ffffffe0002d814e>] vsnprintf+0x2ae/0x3f0
>> [  388.058956] SMP: stopping secondary CPUs
>>
>> Signed-off-by: Tong Tiangen <tongtiangen@huawei.com>
>> ---
>>  arch/riscv/Kconfig                   |   1 +
>>  arch/riscv/include/asm/thread_info.h |  15 ++++
>>  arch/riscv/kernel/entry.S            | 108 +++++++++++++++++++++++++++
>>  arch/riscv/kernel/traps.c            |  35 +++++++++
>>  4 files changed, 159 insertions(+)
>>
>> diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
>> index 4515a10c5d22..587f001e84f4 100644
>> --- a/arch/riscv/Kconfig
>> +++ b/arch/riscv/Kconfig
>> @@ -63,6 +63,7 @@ config RISCV
>>      select HAVE_ARCH_MMAP_RND_BITS if MMU
>>      select HAVE_ARCH_SECCOMP_FILTER
>>      select HAVE_ARCH_TRACEHOOK
>> +    select HAVE_ARCH_VMAP_STACK
>>      select HAVE_ASM_MODVERSIONS
>>      select HAVE_CONTEXT_TRACKING
>>      select HAVE_DEBUG_KMEMLEAK
>> diff --git a/arch/riscv/include/asm/thread_info.h 
>> b/arch/riscv/include/asm/thread_info.h
>> index 0e549a3089b3..60da0dcacf14 100644
>> --- a/arch/riscv/include/asm/thread_info.h
>> +++ b/arch/riscv/include/asm/thread_info.h
>> @@ -19,6 +19,21 @@
>>  #endif
>>  #define THREAD_SIZE        (PAGE_SIZE << THREAD_SIZE_ORDER)
>>
>> +/*
>> + * By aligning VMAP'd stacks to 2 * THREAD_SIZE, we can detect 
>> overflow by
>> + * checking sp & (1 << THREAD_SHIFT), which we can do cheaply in the 
>> entry
>> + * assembly.
>> + */
>> +#ifdef CONFIG_VMAP_STACK
>> +#define THREAD_ALIGN            (2 * THREAD_SIZE)
>> +#else
>> +#define THREAD_ALIGN            THREAD_SIZE
>> +#endif
>> +
>> +#define THREAD_SHIFT            (PAGE_SHIFT + THREAD_SIZE_ORDER)
>> +#define OVERFLOW_STACK_SIZE     SZ_4K
>> +#define SHADOW_OVERFLOW_STACK_SIZE (1024)
>> +
>>  #ifndef __ASSEMBLY__
>>
>>  #include <asm/processor.h>
>> diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S
>> index 83095faa680e..deadf4000b86 100644
>> --- a/arch/riscv/kernel/entry.S
>> +++ b/arch/riscv/kernel/entry.S
>> @@ -29,6 +29,15 @@ ENTRY(handle_exception)
>>  _restore_kernel_tpsp:
>>      csrr tp, CSR_SCRATCH
>>      REG_S sp, TASK_TI_KERNEL_SP(tp)
>> +
>> +#ifdef CONFIG_VMAP_STACK
>> +    addi sp, sp, -(PT_SIZE_ON_STACK)
>> +    srli sp, sp, THREAD_SHIFT
>> +    andi sp, sp, 0x1
>> +    bnez sp, handle_kernel_stack_overflow
>> +    REG_L sp, TASK_TI_KERNEL_SP(tp)
>> +#endif
>> +
>>  _save_context:
>>      REG_S sp, TASK_TI_USER_SP(tp)
>>      REG_L sp, TASK_TI_KERNEL_SP(tp)
>> @@ -375,6 +384,105 @@ handle_syscall_trace_exit:
>>      call do_syscall_trace_exit
>>      j ret_from_exception
>>
>> +#ifdef CONFIG_VMAP_STACK
>> +handle_kernel_stack_overflow:
>> +    la sp, shadow_stack
>> +    addi sp, sp, SHADOW_OVERFLOW_STACK_SIZE
>> +
>> +    //save caller register to shadow stack
>> +    addi sp, sp, -(PT_SIZE_ON_STACK)
>> +    REG_S x1,  PT_RA(sp)
>> +    REG_S x5,  PT_T0(sp)
>> +    REG_S x6,  PT_T1(sp)
>> +    REG_S x7,  PT_T2(sp)
>> +    REG_S x10, PT_A0(sp)
>> +    REG_S x11, PT_A1(sp)
>> +    REG_S x12, PT_A2(sp)
>> +    REG_S x13, PT_A3(sp)
>> +    REG_S x14, PT_A4(sp)
>> +    REG_S x15, PT_A5(sp)
>> +    REG_S x16, PT_A6(sp)
>> +    REG_S x17, PT_A7(sp)
>> +    REG_S x28, PT_T3(sp)
>> +    REG_S x29, PT_T4(sp)
>> +    REG_S x30, PT_T5(sp)
>> +    REG_S x31, PT_T6(sp)
>> +
>> +    la ra, restore_caller_reg
>> +    tail get_overflow_stack
>> +
>> +restore_caller_reg:
>> +    //save per-cpu overflow stack
>> +    sd a0, -8(sp)
>> +    //restore caller register from shadow_stack
>> +    REG_L x1,  PT_RA(sp)
>> +    REG_L x5,  PT_T0(sp)
>> +    REG_L x6,  PT_T1(sp)
>> +    REG_L x7,  PT_T2(sp)
>> +    REG_L x10, PT_A0(sp)
>> +    REG_L x11, PT_A1(sp)
>> +    REG_L x12, PT_A2(sp)
>> +    REG_L x13, PT_A3(sp)
>> +    REG_L x14, PT_A4(sp)
>> +    REG_L x15, PT_A5(sp)
>> +    REG_L x16, PT_A6(sp)
>> +    REG_L x17, PT_A7(sp)
>> +    REG_L x28, PT_T3(sp)
>> +    REG_L x29, PT_T4(sp)
>> +    REG_L x30, PT_T5(sp)
>> +    REG_L x31, PT_T6(sp)
>> +
>> +    //load per-cpu overflow stack
>> +    ld sp, -8(sp)
>> +    addi sp, sp, -(PT_SIZE_ON_STACK)
>> +
>> +    //save context to overflow stack
>> +    REG_S x1,  PT_RA(sp)
>> +    REG_S x3,  PT_GP(sp)
>> +    REG_S x5,  PT_T0(sp)
>> +    REG_S x6,  PT_T1(sp)
>> +    REG_S x7,  PT_T2(sp)
>> +    REG_S x8,  PT_S0(sp)
>> +    REG_S x9,  PT_S1(sp)
>> +    REG_S x10, PT_A0(sp)
>> +    REG_S x11, PT_A1(sp)
>> +    REG_S x12, PT_A2(sp)
>> +    REG_S x13, PT_A3(sp)
>> +    REG_S x14, PT_A4(sp)
>> +    REG_S x15, PT_A5(sp)
>> +    REG_S x16, PT_A6(sp)
>> +    REG_S x17, PT_A7(sp)
>> +    REG_S x18, PT_S2(sp)
>> +    REG_S x19, PT_S3(sp)
>> +    REG_S x20, PT_S4(sp)
>> +    REG_S x21, PT_S5(sp)
>> +    REG_S x22, PT_S6(sp)
>> +    REG_S x23, PT_S7(sp)
>> +    REG_S x24, PT_S8(sp)
>> +    REG_S x25, PT_S9(sp)
>> +    REG_S x26, PT_S10(sp)
>> +    REG_S x27, PT_S11(sp)
>> +    REG_S x28, PT_T3(sp)
>> +    REG_S x29, PT_T4(sp)
>> +    REG_S x30, PT_T5(sp)
>> +    REG_S x31, PT_T6(sp)
>> +
>> +    REG_L s0, TASK_TI_KERNEL_SP(tp)
>> +    csrr s1, CSR_STATUS
>> +    csrr s2, CSR_EPC
>> +    csrr s3, CSR_TVAL
>> +    csrr s4, CSR_CAUSE
>> +    csrr s5, CSR_SCRATCH
>> +    REG_S s0, PT_SP(sp)
>> +    REG_S s1, PT_STATUS(sp)
>> +    REG_S s2, PT_EPC(sp)
>> +    REG_S s3, PT_BADADDR(sp)
>> +    REG_S s4, PT_CAUSE(sp)
>> +    REG_S s5, PT_TP(sp)
>> +    move a0, sp
>> +    tail handle_bad_stack
>> +#endif
>> +
>>  END(handle_exception)
>>
>>  ENTRY(ret_from_fork)
>> diff --git a/arch/riscv/kernel/traps.c b/arch/riscv/kernel/traps.c
>> index 1357abf79570..ff8033b9fdd6 100644
>> --- a/arch/riscv/kernel/traps.c
>> +++ b/arch/riscv/kernel/traps.c
>> @@ -200,3 +200,38 @@ int is_valid_bugaddr(unsigned long pc)
>>  void trap_init(void)
>>  {
>>  }
>> +
>> +#ifdef CONFIG_VMAP_STACK
>> +DEFINE_PER_CPU(unsigned long [OVERFLOW_STACK_SIZE/sizeof(long)], 
>> overflow_stack)
>> +        __aligned(16);
>> +/*
>> + * shadow stack, handled_ kernel_ stack_ overflow(in kernel/entry.S) 
>> is used
>> + * to get per-cpu overflow stack(get_overflow_stack).
>> + */
>> +long shadow_stack[SHADOW_OVERFLOW_STACK_SIZE/sizeof(long)];
>> +asmlinkage unsigned long get_overflow_stack(void)
>> +{
>> +    return (unsigned long)this_cpu_ptr(overflow_stack) +
>> +        OVERFLOW_STACK_SIZE;
>> +}
>> +
>> +asmlinkage void handle_bad_stack(struct pt_regs *regs)
>> +{
>> +    unsigned long tsk_stk = (unsigned long)current->stack;
>> +    unsigned long ovf_stk = (unsigned 
>> long)this_cpu_ptr(overflow_stack);
>> +
>> +    console_verbose();
>> +
>> +    pr_emerg("Insufficient stack space to handle exception!\n");
>> +    pr_emerg("Task stack:     [0x%016lx..0x%016lx]\n",
>> +            tsk_stk, tsk_stk + THREAD_SIZE);
>> +    pr_emerg("Overflow stack: [0x%016lx..0x%016lx]\n",
>> +            ovf_stk, ovf_stk + OVERFLOW_STACK_SIZE);
>> +
>> +    __show_regs(regs);
>> +    panic("Kernel stack overflow");
>> +
>> +    for (;;)
>> +        wait_for_interrupt();
>> +}
>> +#endif
>
> This LGTM.  It seems good enough to take, I'm not sure why this is an 
> RFC?
> .
Thank you for your reply.
The purpose of my RFC is that I don't think the scheme of getting 
per-cpu overflow stack is optimal.
At present, the patch based on next has been issued.



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC PATCH] riscv: add VMAP_STACK overflow detection
@ 2021-05-24 13:51     ` tongtiangen
  0 siblings, 0 replies; 9+ messages in thread
From: tongtiangen @ 2021-05-24 13:51 UTC (permalink / raw)
  To: Palmer Dabbelt; +Cc: Paul Walmsley, aou, linux-riscv, linux-kernel



On 2021/5/23 9:49, Palmer Dabbelt wrote:
> On Fri, 07 May 2021 02:25:09 PDT (-0700), tongtiangen@huawei.com wrote:
>> This patch adds stack overflow detection to riscv, usable when
>> CONFIG_VMAP_STACK=y.
>>
>> Overflow is detected in kernel exception entry(kernel/entry.S), if 
>> the kernel
>> stack is overflow and been detected, the overflow handler is invoked 
>> on a
>> per-cpu overflow stack. This approach preserves GPRs and the original 
>> exception
>> information.
>>
>> The overflow detect is performed before any attempt is made to access 
>> the stack
>> and the principle of stack overflow detection: kernel stacks are 
>> aligned to
>> double their size, enabling overflow to be detected with a single bit 
>> test. For
>> example, a 16K stack is aligned to 32K, ensuring that bit 14 of the 
>> SP must be
>> zero. On an overflow (or underflow), this bit is flipped. Thus, 
>> overflow (of
>> less than the size of the stack) can be detected by testing whether 
>> this bit is
>> set.
>>
>> This gives us a useful error message on stack overflow, as can be 
>> trigger with
>> the LKDTM overflow test:
>>
>> [  388.053267] lkdtm: Performing direct entry EXHAUST_STACK
>> [  388.053663] lkdtm: Calling function with 1024 frame size to depth 
>> 32 ...
>> [  388.054016] lkdtm: loop 32/32 ...
>> [  388.054186] lkdtm: loop 31/32 ...
>> [  388.054491] lkdtm: loop 30/32 ...
>> [  388.054672] lkdtm: loop 29/32 ...
>> [  388.054859] lkdtm: loop 28/32 ...
>> [  388.055010] lkdtm: loop 27/32 ...
>> [  388.055163] lkdtm: loop 26/32 ...
>> [  388.055309] lkdtm: loop 25/32 ...
>> [  388.055481] lkdtm: loop 24/32 ...
>> [  388.055653] lkdtm: loop 23/32 ...
>> [  388.055837] lkdtm: loop 22/32 ...
>> [  388.056015] lkdtm: loop 21/32 ...
>> [  388.056188] lkdtm: loop 20/32 ...
>> [  388.058145] Insufficient stack space to handle exception!
>> [  388.058153] Task stack: [0xffffffd014260000..0xffffffd014264000]
>> [  388.058160] Overflow stack: [0xffffffe1f8d2c220..0xffffffe1f8d2d220]
>> [  388.058168] CPU: 0 PID: 89 Comm: bash Not tainted 5.12.0-rc8-dirty 
>> #90
>> [  388.058175] Hardware name: riscv-virtio,qemu (DT)
>> [  388.058187] epc : number+0x32/0x2c0
>> [  388.058247]  ra : vsnprintf+0x2ae/0x3f0
>> [  388.058255] epc : ffffffe0002d38f6 ra : ffffffe0002d814e sp : 
>> ffffffd01425ffc0
>> [  388.058263]  gp : ffffffe0012e4010 tp : ffffffe08014da00 t0 : 
>> ffffffd0142606e8
>> [  388.058271]  t1 : 0000000000000000 t2 : 0000000000000000 s0 : 
>> ffffffd014260070
>> [  388.058303]  s1 : ffffffd014260158 a0 : ffffffd01426015e a1 : 
>> ffffffd014260158
>> [  388.058311]  a2 : 0000000000000013 a3 : ffff0a01ffffff10 a4 : 
>> ffffffe000c398e0
>> [  388.058319]  a5 : 511b02ec65f3e300 a6 : 0000000000a1749a a7 : 
>> 0000000000000000
>> [  388.058327]  s2 : ffffffff000000ff s3 : 00000000ffff0a01 s4 : 
>> ffffffe0012e50a8
>> [  388.058335]  s5 : 0000000000ffff0a s6 : ffffffe0012e50a8 s7 : 
>> ffffffe000da1cc0
>> [  388.058343]  s8 : ffffffffffffffff s9 : ffffffd0142602b0 s10: 
>> ffffffd0142602a8
>> [  388.058351]  s11: ffffffd01426015e t3 : 00000000000f0000 t4 : 
>> ffffffffffffffff
>> [  388.058359]  t5 : 000000000000002f t6 : ffffffd014260158
>> [  388.058366] status: 0000000000000100 badaddr: ffffffd01425fff8 
>> cause: 000000000000000f
>> [  388.058374] Kernel panic - not syncing: Kernel stack overflow
>> [  388.058381] CPU: 0 PID: 89 Comm: bash Not tainted 5.12.0-rc8-dirty 
>> #90
>> [  388.058387] Hardware name: riscv-virtio,qemu (DT)
>> [  388.058393] Call Trace:
>> [  388.058400] [<ffffffe000004944>] walk_stackframe+0x0/0xce
>> [  388.058406] [<ffffffe0006f0b28>] dump_backtrace+0x38/0x46
>> [  388.058412] [<ffffffe0006f0b46>] show_stack+0x10/0x18
>> [  388.058418] [<ffffffe0006f3690>] dump_stack+0x74/0x8e
>> [  388.058424] [<ffffffe0006f0d52>] panic+0xfc/0x2b2
>> [  388.058430] [<ffffffe0006f0acc>] print_trace_address+0x0/0x24
>> [  388.058436] [<ffffffe0002d814e>] vsnprintf+0x2ae/0x3f0
>> [  388.058956] SMP: stopping secondary CPUs
>>
>> Signed-off-by: Tong Tiangen <tongtiangen@huawei.com>
>> ---
>>  arch/riscv/Kconfig                   |   1 +
>>  arch/riscv/include/asm/thread_info.h |  15 ++++
>>  arch/riscv/kernel/entry.S            | 108 +++++++++++++++++++++++++++
>>  arch/riscv/kernel/traps.c            |  35 +++++++++
>>  4 files changed, 159 insertions(+)
>>
>> diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
>> index 4515a10c5d22..587f001e84f4 100644
>> --- a/arch/riscv/Kconfig
>> +++ b/arch/riscv/Kconfig
>> @@ -63,6 +63,7 @@ config RISCV
>>      select HAVE_ARCH_MMAP_RND_BITS if MMU
>>      select HAVE_ARCH_SECCOMP_FILTER
>>      select HAVE_ARCH_TRACEHOOK
>> +    select HAVE_ARCH_VMAP_STACK
>>      select HAVE_ASM_MODVERSIONS
>>      select HAVE_CONTEXT_TRACKING
>>      select HAVE_DEBUG_KMEMLEAK
>> diff --git a/arch/riscv/include/asm/thread_info.h 
>> b/arch/riscv/include/asm/thread_info.h
>> index 0e549a3089b3..60da0dcacf14 100644
>> --- a/arch/riscv/include/asm/thread_info.h
>> +++ b/arch/riscv/include/asm/thread_info.h
>> @@ -19,6 +19,21 @@
>>  #endif
>>  #define THREAD_SIZE        (PAGE_SIZE << THREAD_SIZE_ORDER)
>>
>> +/*
>> + * By aligning VMAP'd stacks to 2 * THREAD_SIZE, we can detect 
>> overflow by
>> + * checking sp & (1 << THREAD_SHIFT), which we can do cheaply in the 
>> entry
>> + * assembly.
>> + */
>> +#ifdef CONFIG_VMAP_STACK
>> +#define THREAD_ALIGN            (2 * THREAD_SIZE)
>> +#else
>> +#define THREAD_ALIGN            THREAD_SIZE
>> +#endif
>> +
>> +#define THREAD_SHIFT            (PAGE_SHIFT + THREAD_SIZE_ORDER)
>> +#define OVERFLOW_STACK_SIZE     SZ_4K
>> +#define SHADOW_OVERFLOW_STACK_SIZE (1024)
>> +
>>  #ifndef __ASSEMBLY__
>>
>>  #include <asm/processor.h>
>> diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S
>> index 83095faa680e..deadf4000b86 100644
>> --- a/arch/riscv/kernel/entry.S
>> +++ b/arch/riscv/kernel/entry.S
>> @@ -29,6 +29,15 @@ ENTRY(handle_exception)
>>  _restore_kernel_tpsp:
>>      csrr tp, CSR_SCRATCH
>>      REG_S sp, TASK_TI_KERNEL_SP(tp)
>> +
>> +#ifdef CONFIG_VMAP_STACK
>> +    addi sp, sp, -(PT_SIZE_ON_STACK)
>> +    srli sp, sp, THREAD_SHIFT
>> +    andi sp, sp, 0x1
>> +    bnez sp, handle_kernel_stack_overflow
>> +    REG_L sp, TASK_TI_KERNEL_SP(tp)
>> +#endif
>> +
>>  _save_context:
>>      REG_S sp, TASK_TI_USER_SP(tp)
>>      REG_L sp, TASK_TI_KERNEL_SP(tp)
>> @@ -375,6 +384,105 @@ handle_syscall_trace_exit:
>>      call do_syscall_trace_exit
>>      j ret_from_exception
>>
>> +#ifdef CONFIG_VMAP_STACK
>> +handle_kernel_stack_overflow:
>> +    la sp, shadow_stack
>> +    addi sp, sp, SHADOW_OVERFLOW_STACK_SIZE
>> +
>> +    //save caller register to shadow stack
>> +    addi sp, sp, -(PT_SIZE_ON_STACK)
>> +    REG_S x1,  PT_RA(sp)
>> +    REG_S x5,  PT_T0(sp)
>> +    REG_S x6,  PT_T1(sp)
>> +    REG_S x7,  PT_T2(sp)
>> +    REG_S x10, PT_A0(sp)
>> +    REG_S x11, PT_A1(sp)
>> +    REG_S x12, PT_A2(sp)
>> +    REG_S x13, PT_A3(sp)
>> +    REG_S x14, PT_A4(sp)
>> +    REG_S x15, PT_A5(sp)
>> +    REG_S x16, PT_A6(sp)
>> +    REG_S x17, PT_A7(sp)
>> +    REG_S x28, PT_T3(sp)
>> +    REG_S x29, PT_T4(sp)
>> +    REG_S x30, PT_T5(sp)
>> +    REG_S x31, PT_T6(sp)
>> +
>> +    la ra, restore_caller_reg
>> +    tail get_overflow_stack
>> +
>> +restore_caller_reg:
>> +    //save per-cpu overflow stack
>> +    sd a0, -8(sp)
>> +    //restore caller register from shadow_stack
>> +    REG_L x1,  PT_RA(sp)
>> +    REG_L x5,  PT_T0(sp)
>> +    REG_L x6,  PT_T1(sp)
>> +    REG_L x7,  PT_T2(sp)
>> +    REG_L x10, PT_A0(sp)
>> +    REG_L x11, PT_A1(sp)
>> +    REG_L x12, PT_A2(sp)
>> +    REG_L x13, PT_A3(sp)
>> +    REG_L x14, PT_A4(sp)
>> +    REG_L x15, PT_A5(sp)
>> +    REG_L x16, PT_A6(sp)
>> +    REG_L x17, PT_A7(sp)
>> +    REG_L x28, PT_T3(sp)
>> +    REG_L x29, PT_T4(sp)
>> +    REG_L x30, PT_T5(sp)
>> +    REG_L x31, PT_T6(sp)
>> +
>> +    //load per-cpu overflow stack
>> +    ld sp, -8(sp)
>> +    addi sp, sp, -(PT_SIZE_ON_STACK)
>> +
>> +    //save context to overflow stack
>> +    REG_S x1,  PT_RA(sp)
>> +    REG_S x3,  PT_GP(sp)
>> +    REG_S x5,  PT_T0(sp)
>> +    REG_S x6,  PT_T1(sp)
>> +    REG_S x7,  PT_T2(sp)
>> +    REG_S x8,  PT_S0(sp)
>> +    REG_S x9,  PT_S1(sp)
>> +    REG_S x10, PT_A0(sp)
>> +    REG_S x11, PT_A1(sp)
>> +    REG_S x12, PT_A2(sp)
>> +    REG_S x13, PT_A3(sp)
>> +    REG_S x14, PT_A4(sp)
>> +    REG_S x15, PT_A5(sp)
>> +    REG_S x16, PT_A6(sp)
>> +    REG_S x17, PT_A7(sp)
>> +    REG_S x18, PT_S2(sp)
>> +    REG_S x19, PT_S3(sp)
>> +    REG_S x20, PT_S4(sp)
>> +    REG_S x21, PT_S5(sp)
>> +    REG_S x22, PT_S6(sp)
>> +    REG_S x23, PT_S7(sp)
>> +    REG_S x24, PT_S8(sp)
>> +    REG_S x25, PT_S9(sp)
>> +    REG_S x26, PT_S10(sp)
>> +    REG_S x27, PT_S11(sp)
>> +    REG_S x28, PT_T3(sp)
>> +    REG_S x29, PT_T4(sp)
>> +    REG_S x30, PT_T5(sp)
>> +    REG_S x31, PT_T6(sp)
>> +
>> +    REG_L s0, TASK_TI_KERNEL_SP(tp)
>> +    csrr s1, CSR_STATUS
>> +    csrr s2, CSR_EPC
>> +    csrr s3, CSR_TVAL
>> +    csrr s4, CSR_CAUSE
>> +    csrr s5, CSR_SCRATCH
>> +    REG_S s0, PT_SP(sp)
>> +    REG_S s1, PT_STATUS(sp)
>> +    REG_S s2, PT_EPC(sp)
>> +    REG_S s3, PT_BADADDR(sp)
>> +    REG_S s4, PT_CAUSE(sp)
>> +    REG_S s5, PT_TP(sp)
>> +    move a0, sp
>> +    tail handle_bad_stack
>> +#endif
>> +
>>  END(handle_exception)
>>
>>  ENTRY(ret_from_fork)
>> diff --git a/arch/riscv/kernel/traps.c b/arch/riscv/kernel/traps.c
>> index 1357abf79570..ff8033b9fdd6 100644
>> --- a/arch/riscv/kernel/traps.c
>> +++ b/arch/riscv/kernel/traps.c
>> @@ -200,3 +200,38 @@ int is_valid_bugaddr(unsigned long pc)
>>  void trap_init(void)
>>  {
>>  }
>> +
>> +#ifdef CONFIG_VMAP_STACK
>> +DEFINE_PER_CPU(unsigned long [OVERFLOW_STACK_SIZE/sizeof(long)], 
>> overflow_stack)
>> +        __aligned(16);
>> +/*
>> + * shadow stack, handled_ kernel_ stack_ overflow(in kernel/entry.S) 
>> is used
>> + * to get per-cpu overflow stack(get_overflow_stack).
>> + */
>> +long shadow_stack[SHADOW_OVERFLOW_STACK_SIZE/sizeof(long)];
>> +asmlinkage unsigned long get_overflow_stack(void)
>> +{
>> +    return (unsigned long)this_cpu_ptr(overflow_stack) +
>> +        OVERFLOW_STACK_SIZE;
>> +}
>> +
>> +asmlinkage void handle_bad_stack(struct pt_regs *regs)
>> +{
>> +    unsigned long tsk_stk = (unsigned long)current->stack;
>> +    unsigned long ovf_stk = (unsigned 
>> long)this_cpu_ptr(overflow_stack);
>> +
>> +    console_verbose();
>> +
>> +    pr_emerg("Insufficient stack space to handle exception!\n");
>> +    pr_emerg("Task stack:     [0x%016lx..0x%016lx]\n",
>> +            tsk_stk, tsk_stk + THREAD_SIZE);
>> +    pr_emerg("Overflow stack: [0x%016lx..0x%016lx]\n",
>> +            ovf_stk, ovf_stk + OVERFLOW_STACK_SIZE);
>> +
>> +    __show_regs(regs);
>> +    panic("Kernel stack overflow");
>> +
>> +    for (;;)
>> +        wait_for_interrupt();
>> +}
>> +#endif
>
> This LGTM.  It seems good enough to take, I'm not sure why this is an 
> RFC?
> .
Thank you for your reply.
The purpose of my RFC is that I don't think the scheme of getting 
per-cpu overflow stack is optimal.
At present, the patch based on next has been issued.



_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2021-05-24 22:43 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-05-07  9:25 [RFC PATCH] riscv: add VMAP_STACK overflow detection Tong Tiangen
2021-05-07  9:25 ` Tong Tiangen
2021-05-07 12:00 ` kernel test robot
2021-05-07 12:37 ` kernel test robot
2021-05-07 13:22 ` kernel test robot
2021-05-23  1:49 ` Palmer Dabbelt
2021-05-23  1:49   ` Palmer Dabbelt
2021-05-24 13:51   ` tongtiangen
2021-05-24 13:51     ` tongtiangen

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.