2019-05-29 17:18:00 +03:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2017-07-11 04:00:26 +03:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2012 Regents of the University of California
|
|
|
|
*/
|
|
|
|
|
2019-10-18 01:21:28 +03:00
|
|
|
#include <linux/cpu.h>
|
2017-07-11 04:00:26 +03:00
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/sched.h>
|
|
|
|
#include <linux/sched/debug.h>
|
|
|
|
#include <linux/sched/signal.h>
|
|
|
|
#include <linux/signal.h>
|
|
|
|
#include <linux/kdebug.h>
|
|
|
|
#include <linux/uaccess.h>
|
2020-12-17 19:01:42 +03:00
|
|
|
#include <linux/kprobes.h>
|
2017-07-11 04:00:26 +03:00
|
|
|
#include <linux/mm.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/irq.h>
|
|
|
|
|
2021-03-05 14:33:24 +03:00
|
|
|
#include <asm/asm-prototypes.h>
|
2021-01-11 15:40:12 +03:00
|
|
|
#include <asm/bug.h>
|
2017-07-11 04:00:26 +03:00
|
|
|
#include <asm/processor.h>
|
|
|
|
#include <asm/ptrace.h>
|
|
|
|
#include <asm/csr.h>
|
|
|
|
|
|
|
|
int show_unhandled_signals = 1;
|
|
|
|
|
|
|
|
static DEFINE_SPINLOCK(die_lock);
|
|
|
|
|
|
|
|
void die(struct pt_regs *regs, const char *str)
|
|
|
|
{
|
|
|
|
static int die_counter;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
oops_enter();
|
|
|
|
|
|
|
|
spin_lock_irq(&die_lock);
|
|
|
|
console_verbose();
|
|
|
|
bust_spinlocks(1);
|
|
|
|
|
|
|
|
pr_emerg("%s [#%d]\n", str, ++die_counter);
|
|
|
|
print_modules();
|
|
|
|
show_regs(regs);
|
|
|
|
|
2019-10-28 15:10:32 +03:00
|
|
|
ret = notify_die(DIE_OOPS, str, regs, 0, regs->cause, SIGSEGV);
|
2017-07-11 04:00:26 +03:00
|
|
|
|
|
|
|
bust_spinlocks(0);
|
|
|
|
add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE);
|
|
|
|
spin_unlock_irq(&die_lock);
|
|
|
|
oops_exit();
|
|
|
|
|
|
|
|
if (in_interrupt())
|
|
|
|
panic("Fatal exception in interrupt");
|
|
|
|
if (panic_on_oops)
|
|
|
|
panic("Fatal exception");
|
|
|
|
if (ret != NOTIFY_STOP)
|
2021-06-28 22:52:01 +03:00
|
|
|
make_task_dead(SIGSEGV);
|
2017-07-11 04:00:26 +03:00
|
|
|
}
|
|
|
|
|
2019-02-06 04:10:48 +03:00
|
|
|
void do_trap(struct pt_regs *regs, int signo, int code, unsigned long addr)
|
2017-07-11 04:00:26 +03:00
|
|
|
{
|
2019-02-06 04:10:48 +03:00
|
|
|
struct task_struct *tsk = current;
|
|
|
|
|
2017-07-11 04:00:26 +03:00
|
|
|
if (show_unhandled_signals && unhandled_signal(tsk, signo)
|
|
|
|
&& printk_ratelimit()) {
|
|
|
|
pr_info("%s[%d]: unhandled signal %d code 0x%x at 0x" REG_FMT,
|
|
|
|
tsk->comm, task_pid_nr(tsk), signo, code, addr);
|
2019-04-15 12:14:40 +03:00
|
|
|
print_vma_addr(KERN_CONT " in ", instruction_pointer(regs));
|
2017-07-11 04:00:26 +03:00
|
|
|
pr_cont("\n");
|
2021-01-11 15:40:12 +03:00
|
|
|
__show_regs(regs);
|
2017-07-11 04:00:26 +03:00
|
|
|
}
|
|
|
|
|
2019-05-23 19:04:24 +03:00
|
|
|
force_sig_fault(signo, code, (void __user *)addr);
|
2017-07-11 04:00:26 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void do_trap_error(struct pt_regs *regs, int signo, int code,
|
|
|
|
unsigned long addr, const char *str)
|
|
|
|
{
|
2020-12-17 19:01:44 +03:00
|
|
|
current->thread.bad_cause = regs->cause;
|
|
|
|
|
2017-07-11 04:00:26 +03:00
|
|
|
if (user_mode(regs)) {
|
2019-02-06 04:10:48 +03:00
|
|
|
do_trap(regs, signo, code, addr);
|
2017-07-11 04:00:26 +03:00
|
|
|
} else {
|
|
|
|
if (!fixup_exception(regs))
|
|
|
|
die(regs, str);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-31 11:53:42 +03:00
|
|
|
#if defined (CONFIG_XIP_KERNEL) && defined (CONFIG_RISCV_ERRATA_ALTERNATIVE)
|
|
|
|
#define __trap_section __section(".xip.traps")
|
|
|
|
#else
|
|
|
|
#define __trap_section
|
|
|
|
#endif
|
2017-07-11 04:00:26 +03:00
|
|
|
#define DO_ERROR_INFO(name, signo, code, str) \
|
2021-05-31 11:53:42 +03:00
|
|
|
asmlinkage __visible __trap_section void name(struct pt_regs *regs) \
|
2017-07-11 04:00:26 +03:00
|
|
|
{ \
|
2019-10-28 15:10:32 +03:00
|
|
|
do_trap_error(regs, signo, code, regs->epc, "Oops - " str); \
|
2017-07-11 04:00:26 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
DO_ERROR_INFO(do_trap_unknown,
|
|
|
|
SIGILL, ILL_ILLTRP, "unknown exception");
|
|
|
|
DO_ERROR_INFO(do_trap_insn_misaligned,
|
|
|
|
SIGBUS, BUS_ADRALN, "instruction address misaligned");
|
|
|
|
DO_ERROR_INFO(do_trap_insn_fault,
|
|
|
|
SIGSEGV, SEGV_ACCERR, "instruction access fault");
|
|
|
|
DO_ERROR_INFO(do_trap_insn_illegal,
|
|
|
|
SIGILL, ILL_ILLOPC, "illegal instruction");
|
|
|
|
DO_ERROR_INFO(do_trap_load_fault,
|
|
|
|
SIGSEGV, SEGV_ACCERR, "load access fault");
|
2020-03-16 03:47:36 +03:00
|
|
|
#ifndef CONFIG_RISCV_M_MODE
|
|
|
|
DO_ERROR_INFO(do_trap_load_misaligned,
|
|
|
|
SIGBUS, BUS_ADRALN, "Oops - load address misaligned");
|
2017-07-11 04:00:26 +03:00
|
|
|
DO_ERROR_INFO(do_trap_store_misaligned,
|
2020-03-16 03:47:36 +03:00
|
|
|
SIGBUS, BUS_ADRALN, "Oops - store (or AMO) address misaligned");
|
|
|
|
#else
|
|
|
|
int handle_misaligned_load(struct pt_regs *regs);
|
|
|
|
int handle_misaligned_store(struct pt_regs *regs);
|
|
|
|
|
2021-05-31 11:53:42 +03:00
|
|
|
asmlinkage void __trap_section do_trap_load_misaligned(struct pt_regs *regs)
|
2020-03-16 03:47:36 +03:00
|
|
|
{
|
|
|
|
if (!handle_misaligned_load(regs))
|
|
|
|
return;
|
|
|
|
do_trap_error(regs, SIGBUS, BUS_ADRALN, regs->epc,
|
|
|
|
"Oops - load address misaligned");
|
|
|
|
}
|
|
|
|
|
2021-05-31 11:53:42 +03:00
|
|
|
asmlinkage void __trap_section do_trap_store_misaligned(struct pt_regs *regs)
|
2020-03-16 03:47:36 +03:00
|
|
|
{
|
|
|
|
if (!handle_misaligned_store(regs))
|
|
|
|
return;
|
|
|
|
do_trap_error(regs, SIGBUS, BUS_ADRALN, regs->epc,
|
|
|
|
"Oops - store (or AMO) address misaligned");
|
|
|
|
}
|
|
|
|
#endif
|
2017-07-11 04:00:26 +03:00
|
|
|
DO_ERROR_INFO(do_trap_store_fault,
|
|
|
|
SIGSEGV, SEGV_ACCERR, "store (or AMO) access fault");
|
|
|
|
DO_ERROR_INFO(do_trap_ecall_u,
|
|
|
|
SIGILL, ILL_ILLTRP, "environment call from U-mode");
|
|
|
|
DO_ERROR_INFO(do_trap_ecall_s,
|
|
|
|
SIGILL, ILL_ILLTRP, "environment call from S-mode");
|
|
|
|
DO_ERROR_INFO(do_trap_ecall_m,
|
|
|
|
SIGILL, ILL_ILLTRP, "environment call from M-mode");
|
|
|
|
|
2019-03-05 06:23:34 +03:00
|
|
|
static inline unsigned long get_break_insn_length(unsigned long pc)
|
|
|
|
{
|
|
|
|
bug_insn_t insn;
|
|
|
|
|
2020-06-17 10:37:55 +03:00
|
|
|
if (get_kernel_nofault(insn, (bug_insn_t *)pc))
|
2019-03-05 06:23:34 +03:00
|
|
|
return 0;
|
2020-03-09 19:55:42 +03:00
|
|
|
|
|
|
|
return GET_INSN_LENGTH(insn);
|
2019-03-05 06:23:34 +03:00
|
|
|
}
|
|
|
|
|
2021-05-31 11:53:42 +03:00
|
|
|
asmlinkage __visible __trap_section void do_trap_break(struct pt_regs *regs)
|
2017-07-11 04:00:26 +03:00
|
|
|
{
|
2020-12-17 19:01:42 +03:00
|
|
|
#ifdef CONFIG_KPROBES
|
|
|
|
if (kprobe_single_step_handler(regs))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (kprobe_breakpoint_handler(regs))
|
|
|
|
return;
|
|
|
|
#endif
|
2020-12-17 19:01:44 +03:00
|
|
|
#ifdef CONFIG_UPROBES
|
|
|
|
if (uprobe_single_step_handler(regs))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (uprobe_breakpoint_handler(regs))
|
|
|
|
return;
|
|
|
|
#endif
|
|
|
|
current->thread.bad_cause = regs->cause;
|
2020-12-17 19:01:42 +03:00
|
|
|
|
2019-10-17 20:37:30 +03:00
|
|
|
if (user_mode(regs))
|
2019-10-28 15:10:32 +03:00
|
|
|
force_sig_fault(SIGTRAP, TRAP_BRKPT, (void __user *)regs->epc);
|
2020-04-16 05:38:05 +03:00
|
|
|
#ifdef CONFIG_KGDB
|
|
|
|
else if (notify_die(DIE_TRAP, "EBREAK", regs, 0, regs->cause, SIGTRAP)
|
|
|
|
== NOTIFY_STOP)
|
|
|
|
return;
|
|
|
|
#endif
|
2019-10-28 15:10:32 +03:00
|
|
|
else if (report_bug(regs->epc, regs) == BUG_TRAP_TYPE_WARN)
|
|
|
|
regs->epc += get_break_insn_length(regs->epc);
|
2019-10-17 20:37:30 +03:00
|
|
|
else
|
|
|
|
die(regs, "Kernel BUG");
|
2017-07-11 04:00:26 +03:00
|
|
|
}
|
2021-03-29 21:12:26 +03:00
|
|
|
NOKPROBE_SYMBOL(do_trap_break);
|
2017-07-11 04:00:26 +03:00
|
|
|
|
|
|
|
#ifdef CONFIG_GENERIC_BUG
|
|
|
|
int is_valid_bugaddr(unsigned long pc)
|
|
|
|
{
|
|
|
|
bug_insn_t insn;
|
|
|
|
|
2019-03-05 06:23:35 +03:00
|
|
|
if (pc < VMALLOC_START)
|
2017-07-11 04:00:26 +03:00
|
|
|
return 0;
|
2020-06-17 10:37:55 +03:00
|
|
|
if (get_kernel_nofault(insn, (bug_insn_t *)pc))
|
2017-07-11 04:00:26 +03:00
|
|
|
return 0;
|
2019-03-05 06:23:34 +03:00
|
|
|
if ((insn & __INSN_LENGTH_MASK) == __INSN_LENGTH_32)
|
|
|
|
return (insn == __BUG_INSN_32);
|
|
|
|
else
|
|
|
|
return ((insn & __COMPRESSED_INSN_MASK) == __BUG_INSN_16);
|
2017-07-11 04:00:26 +03:00
|
|
|
}
|
|
|
|
#endif /* CONFIG_GENERIC_BUG */
|
|
|
|
|
riscv: add VMAP_STACK overflow detection
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>
Reviewed-by: Kefeng Wang <wangkefeng.wang@huawei.com>
Signed-off-by: Palmer Dabbelt <palmerdabbelt@google.com>
2021-06-21 06:28:55 +03:00
|
|
|
#ifdef CONFIG_VMAP_STACK
|
|
|
|
static 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
|