2019-05-29 07:18:00 -07:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2017-07-10 18:00:26 -07:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2012 Regents of the University of California
|
|
|
|
*/
|
|
|
|
|
2019-10-17 15:21:28 -07:00
|
|
|
#include <linux/cpu.h>
|
2017-07-10 18:00:26 -07:00
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/init.h>
|
2023-11-09 06:37:51 -07:00
|
|
|
#include <linux/randomize_kstack.h>
|
2017-07-10 18:00:26 -07:00
|
|
|
#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 09:01:42 -07:00
|
|
|
#include <linux/kprobes.h>
|
riscv: Only consider swbp/ss handlers for correct privileged mode
RISC-V software breakpoint trap handlers are used for {k,u}probes.
When trapping from kernelmode, only the kernelmode handlers should be
considered. Vice versa, only usermode handlers for usermode
traps. This is not the case on RISC-V, which can trigger a bug if a
userspace process uses uprobes, and a WARN() is triggered from
kernelmode (which is implemented via {c.,}ebreak).
The kernel will trap on the kernelmode {c.,}ebreak, look for uprobes
handlers, realize incorrectly that uprobes need to be handled, and
exit the trap handler early. The trap returns to re-executing the
{c.,}ebreak, and enter an infinite trap-loop.
The issue was found running the BPF selftest [1].
Fix this issue by only considering the swbp/ss handlers for
kernel/usermode respectively. Also, move CONFIG ifdeffery from traps.c
to the asm/{k,u}probes.h headers.
Note that linux/uprobes.h only include asm/uprobes.h if CONFIG_UPROBES
is defined, which is why asm/uprobes.h needs to be unconditionally
included in traps.c
Link: https://lore.kernel.org/linux-riscv/87v8d19aun.fsf@all.your.base.are.belong.to.us/ # [1]
Fixes: 74784081aac8 ("riscv: Add uprobes supported")
Reviewed-by: Guo Ren <guoren@kernel.org>
Reviewed-by: Nam Cao <namcaov@gmail.com>
Tested-by: Puranjay Mohan <puranjay12@gmail.com>
Signed-off-by: Björn Töpel <bjorn@rivosinc.com>
Link: https://lore.kernel.org/r/20230912065619.62020-1-bjorn@kernel.org
Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
2023-09-11 23:56:19 -07:00
|
|
|
#include <linux/uprobes.h>
|
|
|
|
#include <asm/uprobes.h>
|
2017-07-10 18:00:26 -07:00
|
|
|
#include <linux/mm.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/irq.h>
|
2022-06-06 01:23:08 -07:00
|
|
|
#include <linux/kexec.h>
|
2023-02-21 20:30:18 -07:00
|
|
|
#include <linux/entry-common.h>
|
2017-07-10 18:00:26 -07:00
|
|
|
|
2021-03-05 04:33:24 -07:00
|
|
|
#include <asm/asm-prototypes.h>
|
2021-01-11 05:40:12 -07:00
|
|
|
#include <asm/bug.h>
|
2023-07-10 11:35:49 -07:00
|
|
|
#include <asm/cfi.h>
|
2022-08-14 07:12:38 -07:00
|
|
|
#include <asm/csr.h>
|
2017-07-10 18:00:26 -07:00
|
|
|
#include <asm/processor.h>
|
|
|
|
#include <asm/ptrace.h>
|
2023-02-21 20:30:18 -07:00
|
|
|
#include <asm/syscall.h>
|
2022-08-14 07:12:38 -07:00
|
|
|
#include <asm/thread_info.h>
|
2023-06-05 04:07:08 -07:00
|
|
|
#include <asm/vector.h>
|
2023-06-13 18:30:16 -07:00
|
|
|
#include <asm/irq_stack.h>
|
2017-07-10 18:00:26 -07:00
|
|
|
|
|
|
|
int show_unhandled_signals = 1;
|
|
|
|
|
|
|
|
static DEFINE_SPINLOCK(die_lock);
|
|
|
|
|
2023-09-11 19:13:49 -07:00
|
|
|
static int copy_code(struct pt_regs *regs, u16 *val, const u16 *insns)
|
|
|
|
{
|
|
|
|
const void __user *uaddr = (__force const void __user *)insns;
|
|
|
|
|
|
|
|
if (!user_mode(regs))
|
|
|
|
return get_kernel_nofault(*val, insns);
|
|
|
|
|
|
|
|
/* The user space code from other tasks cannot be accessed. */
|
|
|
|
if (regs != task_pt_regs(current))
|
|
|
|
return -EPERM;
|
|
|
|
|
|
|
|
return copy_from_user_nofault(val, uaddr, sizeof(*val));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dump_instr(const char *loglvl, struct pt_regs *regs)
|
2023-01-19 00:47:37 -07:00
|
|
|
{
|
|
|
|
char str[sizeof("0000 ") * 12 + 2 + 1], *p = str;
|
|
|
|
const u16 *insns = (u16 *)instruction_pointer(regs);
|
|
|
|
long bad;
|
|
|
|
u16 val;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = -10; i < 2; i++) {
|
2023-09-11 19:13:49 -07:00
|
|
|
bad = copy_code(regs, &val, &insns[i]);
|
2023-01-19 00:47:37 -07:00
|
|
|
if (!bad) {
|
|
|
|
p += sprintf(p, i == 0 ? "(%04hx) " : "%04hx ", val);
|
|
|
|
} else {
|
|
|
|
printk("%sCode: Unable to access instruction at 0x%px.\n",
|
|
|
|
loglvl, &insns[i]);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
printk("%sCode: %s\n", loglvl, str);
|
|
|
|
}
|
|
|
|
|
2017-07-10 18:00:26 -07:00
|
|
|
void die(struct pt_regs *regs, const char *str)
|
|
|
|
{
|
|
|
|
static int die_counter;
|
|
|
|
int ret;
|
2022-09-20 13:00:37 -07:00
|
|
|
long cause;
|
2023-02-15 07:48:28 -07:00
|
|
|
unsigned long flags;
|
2017-07-10 18:00:26 -07:00
|
|
|
|
|
|
|
oops_enter();
|
|
|
|
|
2023-02-15 07:48:28 -07:00
|
|
|
spin_lock_irqsave(&die_lock, flags);
|
2017-07-10 18:00:26 -07:00
|
|
|
console_verbose();
|
|
|
|
bust_spinlocks(1);
|
|
|
|
|
|
|
|
pr_emerg("%s [#%d]\n", str, ++die_counter);
|
|
|
|
print_modules();
|
2023-01-19 00:47:37 -07:00
|
|
|
if (regs) {
|
2022-09-20 13:00:37 -07:00
|
|
|
show_regs(regs);
|
2023-09-11 19:13:49 -07:00
|
|
|
dump_instr(KERN_EMERG, regs);
|
2023-01-19 00:47:37 -07:00
|
|
|
}
|
2017-07-10 18:00:26 -07:00
|
|
|
|
2022-09-20 13:00:37 -07:00
|
|
|
cause = regs ? regs->cause : -1;
|
|
|
|
ret = notify_die(DIE_OOPS, str, regs, 0, cause, SIGSEGV);
|
2017-07-10 18:00:26 -07:00
|
|
|
|
2022-09-20 13:00:37 -07:00
|
|
|
if (kexec_should_crash(current))
|
2022-06-06 01:23:08 -07:00
|
|
|
crash_kexec(regs);
|
|
|
|
|
2017-07-10 18:00:26 -07:00
|
|
|
bust_spinlocks(0);
|
|
|
|
add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE);
|
2023-02-15 07:48:28 -07:00
|
|
|
spin_unlock_irqrestore(&die_lock, flags);
|
2017-07-10 18:00:26 -07:00
|
|
|
oops_exit();
|
|
|
|
|
|
|
|
if (in_interrupt())
|
|
|
|
panic("Fatal exception in interrupt");
|
|
|
|
if (panic_on_oops)
|
|
|
|
panic("Fatal exception");
|
|
|
|
if (ret != NOTIFY_STOP)
|
2021-06-28 12:52:01 -07:00
|
|
|
make_task_dead(SIGSEGV);
|
2017-07-10 18:00:26 -07:00
|
|
|
}
|
|
|
|
|
2019-02-05 18:10:48 -07:00
|
|
|
void do_trap(struct pt_regs *regs, int signo, int code, unsigned long addr)
|
2017-07-10 18:00:26 -07:00
|
|
|
{
|
2019-02-05 18:10:48 -07:00
|
|
|
struct task_struct *tsk = current;
|
|
|
|
|
2017-07-10 18:00:26 -07: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 02:14:40 -07:00
|
|
|
print_vma_addr(KERN_CONT " in ", instruction_pointer(regs));
|
2017-07-10 18:00:26 -07:00
|
|
|
pr_cont("\n");
|
2021-01-11 05:40:12 -07:00
|
|
|
__show_regs(regs);
|
2024-03-19 08:40:05 -07:00
|
|
|
dump_instr(KERN_INFO, regs);
|
2017-07-10 18:00:26 -07:00
|
|
|
}
|
|
|
|
|
2019-05-23 09:04:24 -07:00
|
|
|
force_sig_fault(signo, code, (void __user *)addr);
|
2017-07-10 18:00:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void do_trap_error(struct pt_regs *regs, int signo, int code,
|
|
|
|
unsigned long addr, const char *str)
|
|
|
|
{
|
2020-12-17 09:01:44 -07:00
|
|
|
current->thread.bad_cause = regs->cause;
|
|
|
|
|
2017-07-10 18:00:26 -07:00
|
|
|
if (user_mode(regs)) {
|
2019-02-05 18:10:48 -07:00
|
|
|
do_trap(regs, signo, code, addr);
|
2017-07-10 18:00:26 -07:00
|
|
|
} else {
|
|
|
|
if (!fixup_exception(regs))
|
|
|
|
die(regs, str);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-11 12:29:10 -07:00
|
|
|
#if defined(CONFIG_XIP_KERNEL) && defined(CONFIG_RISCV_ALTERNATIVE)
|
2023-02-21 20:30:17 -07:00
|
|
|
#define __trap_section __noinstr_section(".xip.traps")
|
2021-05-31 01:53:42 -07:00
|
|
|
#else
|
2023-02-21 20:30:17 -07:00
|
|
|
#define __trap_section noinstr
|
2021-05-31 01:53:42 -07:00
|
|
|
#endif
|
2023-02-21 20:30:18 -07:00
|
|
|
#define DO_ERROR_INFO(name, signo, code, str) \
|
|
|
|
asmlinkage __visible __trap_section void name(struct pt_regs *regs) \
|
|
|
|
{ \
|
|
|
|
if (user_mode(regs)) { \
|
|
|
|
irqentry_enter_from_user_mode(regs); \
|
|
|
|
do_trap_error(regs, signo, code, regs->epc, "Oops - " str); \
|
|
|
|
irqentry_exit_to_user_mode(regs); \
|
|
|
|
} else { \
|
|
|
|
irqentry_state_t state = irqentry_nmi_enter(regs); \
|
|
|
|
do_trap_error(regs, signo, code, regs->epc, "Oops - " str); \
|
|
|
|
irqentry_nmi_exit(regs, state); \
|
|
|
|
} \
|
2017-07-10 18:00:26 -07: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");
|
2023-06-05 04:07:08 -07:00
|
|
|
|
|
|
|
asmlinkage __visible __trap_section void do_trap_insn_illegal(struct pt_regs *regs)
|
|
|
|
{
|
2023-06-25 08:54:15 -07:00
|
|
|
bool handled;
|
|
|
|
|
2023-06-05 04:07:08 -07:00
|
|
|
if (user_mode(regs)) {
|
|
|
|
irqentry_enter_from_user_mode(regs);
|
|
|
|
|
|
|
|
local_irq_enable();
|
|
|
|
|
2023-06-25 08:54:15 -07:00
|
|
|
handled = riscv_v_first_use_handler(regs);
|
|
|
|
|
|
|
|
local_irq_disable();
|
|
|
|
|
|
|
|
if (!handled)
|
2023-06-05 04:07:08 -07:00
|
|
|
do_trap_error(regs, SIGILL, ILL_ILLOPC, regs->epc,
|
|
|
|
"Oops - illegal instruction");
|
|
|
|
|
|
|
|
irqentry_exit_to_user_mode(regs);
|
|
|
|
} else {
|
|
|
|
irqentry_state_t state = irqentry_nmi_enter(regs);
|
|
|
|
|
|
|
|
do_trap_error(regs, SIGILL, ILL_ILLOPC, regs->epc,
|
|
|
|
"Oops - illegal instruction");
|
|
|
|
|
|
|
|
irqentry_nmi_exit(regs, state);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-10 18:00:26 -07:00
|
|
|
DO_ERROR_INFO(do_trap_load_fault,
|
|
|
|
SIGSEGV, SEGV_ACCERR, "load access fault");
|
2020-03-15 17:47:36 -07:00
|
|
|
|
2023-02-21 20:30:18 -07:00
|
|
|
asmlinkage __visible __trap_section void do_trap_load_misaligned(struct pt_regs *regs)
|
2020-03-15 17:47:36 -07:00
|
|
|
{
|
2023-02-21 20:30:18 -07:00
|
|
|
if (user_mode(regs)) {
|
|
|
|
irqentry_enter_from_user_mode(regs);
|
|
|
|
|
|
|
|
if (handle_misaligned_load(regs))
|
|
|
|
do_trap_error(regs, SIGBUS, BUS_ADRALN, regs->epc,
|
|
|
|
"Oops - load address misaligned");
|
|
|
|
|
|
|
|
irqentry_exit_to_user_mode(regs);
|
|
|
|
} else {
|
|
|
|
irqentry_state_t state = irqentry_nmi_enter(regs);
|
|
|
|
|
|
|
|
if (handle_misaligned_load(regs))
|
|
|
|
do_trap_error(regs, SIGBUS, BUS_ADRALN, regs->epc,
|
|
|
|
"Oops - load address misaligned");
|
|
|
|
|
|
|
|
irqentry_nmi_exit(regs, state);
|
|
|
|
}
|
2020-03-15 17:47:36 -07:00
|
|
|
}
|
|
|
|
|
2023-02-21 20:30:18 -07:00
|
|
|
asmlinkage __visible __trap_section void do_trap_store_misaligned(struct pt_regs *regs)
|
2020-03-15 17:47:36 -07:00
|
|
|
{
|
2023-02-21 20:30:18 -07:00
|
|
|
if (user_mode(regs)) {
|
|
|
|
irqentry_enter_from_user_mode(regs);
|
|
|
|
|
|
|
|
if (handle_misaligned_store(regs))
|
|
|
|
do_trap_error(regs, SIGBUS, BUS_ADRALN, regs->epc,
|
|
|
|
"Oops - store (or AMO) address misaligned");
|
|
|
|
|
|
|
|
irqentry_exit_to_user_mode(regs);
|
|
|
|
} else {
|
|
|
|
irqentry_state_t state = irqentry_nmi_enter(regs);
|
|
|
|
|
|
|
|
if (handle_misaligned_store(regs))
|
|
|
|
do_trap_error(regs, SIGBUS, BUS_ADRALN, regs->epc,
|
|
|
|
"Oops - store (or AMO) address misaligned");
|
|
|
|
|
|
|
|
irqentry_nmi_exit(regs, state);
|
|
|
|
}
|
2020-03-15 17:47:36 -07:00
|
|
|
}
|
2017-07-10 18:00:26 -07:00
|
|
|
DO_ERROR_INFO(do_trap_store_fault,
|
|
|
|
SIGSEGV, SEGV_ACCERR, "store (or AMO) access fault");
|
|
|
|
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-04 20:23:34 -07:00
|
|
|
static inline unsigned long get_break_insn_length(unsigned long pc)
|
|
|
|
{
|
|
|
|
bug_insn_t insn;
|
|
|
|
|
2020-06-17 00:37:55 -07:00
|
|
|
if (get_kernel_nofault(insn, (bug_insn_t *)pc))
|
2019-03-04 20:23:34 -07:00
|
|
|
return 0;
|
2020-03-09 09:55:42 -07:00
|
|
|
|
|
|
|
return GET_INSN_LENGTH(insn);
|
2019-03-04 20:23:34 -07:00
|
|
|
}
|
|
|
|
|
riscv: Only consider swbp/ss handlers for correct privileged mode
RISC-V software breakpoint trap handlers are used for {k,u}probes.
When trapping from kernelmode, only the kernelmode handlers should be
considered. Vice versa, only usermode handlers for usermode
traps. This is not the case on RISC-V, which can trigger a bug if a
userspace process uses uprobes, and a WARN() is triggered from
kernelmode (which is implemented via {c.,}ebreak).
The kernel will trap on the kernelmode {c.,}ebreak, look for uprobes
handlers, realize incorrectly that uprobes need to be handled, and
exit the trap handler early. The trap returns to re-executing the
{c.,}ebreak, and enter an infinite trap-loop.
The issue was found running the BPF selftest [1].
Fix this issue by only considering the swbp/ss handlers for
kernel/usermode respectively. Also, move CONFIG ifdeffery from traps.c
to the asm/{k,u}probes.h headers.
Note that linux/uprobes.h only include asm/uprobes.h if CONFIG_UPROBES
is defined, which is why asm/uprobes.h needs to be unconditionally
included in traps.c
Link: https://lore.kernel.org/linux-riscv/87v8d19aun.fsf@all.your.base.are.belong.to.us/ # [1]
Fixes: 74784081aac8 ("riscv: Add uprobes supported")
Reviewed-by: Guo Ren <guoren@kernel.org>
Reviewed-by: Nam Cao <namcaov@gmail.com>
Tested-by: Puranjay Mohan <puranjay12@gmail.com>
Signed-off-by: Björn Töpel <bjorn@rivosinc.com>
Link: https://lore.kernel.org/r/20230912065619.62020-1-bjorn@kernel.org
Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
2023-09-11 23:56:19 -07:00
|
|
|
static bool probe_single_step_handler(struct pt_regs *regs)
|
|
|
|
{
|
|
|
|
bool user = user_mode(regs);
|
|
|
|
|
|
|
|
return user ? uprobe_single_step_handler(regs) : kprobe_single_step_handler(regs);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool probe_breakpoint_handler(struct pt_regs *regs)
|
|
|
|
{
|
|
|
|
bool user = user_mode(regs);
|
|
|
|
|
|
|
|
return user ? uprobe_breakpoint_handler(regs) : kprobe_breakpoint_handler(regs);
|
|
|
|
}
|
|
|
|
|
2023-02-21 20:30:18 -07:00
|
|
|
void handle_break(struct pt_regs *regs)
|
2017-07-10 18:00:26 -07:00
|
|
|
{
|
riscv: Only consider swbp/ss handlers for correct privileged mode
RISC-V software breakpoint trap handlers are used for {k,u}probes.
When trapping from kernelmode, only the kernelmode handlers should be
considered. Vice versa, only usermode handlers for usermode
traps. This is not the case on RISC-V, which can trigger a bug if a
userspace process uses uprobes, and a WARN() is triggered from
kernelmode (which is implemented via {c.,}ebreak).
The kernel will trap on the kernelmode {c.,}ebreak, look for uprobes
handlers, realize incorrectly that uprobes need to be handled, and
exit the trap handler early. The trap returns to re-executing the
{c.,}ebreak, and enter an infinite trap-loop.
The issue was found running the BPF selftest [1].
Fix this issue by only considering the swbp/ss handlers for
kernel/usermode respectively. Also, move CONFIG ifdeffery from traps.c
to the asm/{k,u}probes.h headers.
Note that linux/uprobes.h only include asm/uprobes.h if CONFIG_UPROBES
is defined, which is why asm/uprobes.h needs to be unconditionally
included in traps.c
Link: https://lore.kernel.org/linux-riscv/87v8d19aun.fsf@all.your.base.are.belong.to.us/ # [1]
Fixes: 74784081aac8 ("riscv: Add uprobes supported")
Reviewed-by: Guo Ren <guoren@kernel.org>
Reviewed-by: Nam Cao <namcaov@gmail.com>
Tested-by: Puranjay Mohan <puranjay12@gmail.com>
Signed-off-by: Björn Töpel <bjorn@rivosinc.com>
Link: https://lore.kernel.org/r/20230912065619.62020-1-bjorn@kernel.org
Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
2023-09-11 23:56:19 -07:00
|
|
|
if (probe_single_step_handler(regs))
|
2020-12-17 09:01:42 -07:00
|
|
|
return;
|
|
|
|
|
riscv: Only consider swbp/ss handlers for correct privileged mode
RISC-V software breakpoint trap handlers are used for {k,u}probes.
When trapping from kernelmode, only the kernelmode handlers should be
considered. Vice versa, only usermode handlers for usermode
traps. This is not the case on RISC-V, which can trigger a bug if a
userspace process uses uprobes, and a WARN() is triggered from
kernelmode (which is implemented via {c.,}ebreak).
The kernel will trap on the kernelmode {c.,}ebreak, look for uprobes
handlers, realize incorrectly that uprobes need to be handled, and
exit the trap handler early. The trap returns to re-executing the
{c.,}ebreak, and enter an infinite trap-loop.
The issue was found running the BPF selftest [1].
Fix this issue by only considering the swbp/ss handlers for
kernel/usermode respectively. Also, move CONFIG ifdeffery from traps.c
to the asm/{k,u}probes.h headers.
Note that linux/uprobes.h only include asm/uprobes.h if CONFIG_UPROBES
is defined, which is why asm/uprobes.h needs to be unconditionally
included in traps.c
Link: https://lore.kernel.org/linux-riscv/87v8d19aun.fsf@all.your.base.are.belong.to.us/ # [1]
Fixes: 74784081aac8 ("riscv: Add uprobes supported")
Reviewed-by: Guo Ren <guoren@kernel.org>
Reviewed-by: Nam Cao <namcaov@gmail.com>
Tested-by: Puranjay Mohan <puranjay12@gmail.com>
Signed-off-by: Björn Töpel <bjorn@rivosinc.com>
Link: https://lore.kernel.org/r/20230912065619.62020-1-bjorn@kernel.org
Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
2023-09-11 23:56:19 -07:00
|
|
|
if (probe_breakpoint_handler(regs))
|
2020-12-17 09:01:44 -07:00
|
|
|
return;
|
|
|
|
|
|
|
|
current->thread.bad_cause = regs->cause;
|
2020-12-17 09:01:42 -07:00
|
|
|
|
2019-10-17 10:37:30 -07:00
|
|
|
if (user_mode(regs))
|
2019-10-28 05:10:32 -07:00
|
|
|
force_sig_fault(SIGTRAP, TRAP_BRKPT, (void __user *)regs->epc);
|
2020-04-15 19:38:05 -07:00
|
|
|
#ifdef CONFIG_KGDB
|
|
|
|
else if (notify_die(DIE_TRAP, "EBREAK", regs, 0, regs->cause, SIGTRAP)
|
|
|
|
== NOTIFY_STOP)
|
|
|
|
return;
|
|
|
|
#endif
|
2023-07-10 11:35:49 -07:00
|
|
|
else if (report_bug(regs->epc, regs) == BUG_TRAP_TYPE_WARN ||
|
|
|
|
handle_cfi_failure(regs) == BUG_TRAP_TYPE_WARN)
|
2019-10-28 05:10:32 -07:00
|
|
|
regs->epc += get_break_insn_length(regs->epc);
|
2019-10-17 10:37:30 -07:00
|
|
|
else
|
|
|
|
die(regs, "Kernel BUG");
|
2017-07-10 18:00:26 -07:00
|
|
|
}
|
2023-02-21 20:30:18 -07:00
|
|
|
|
|
|
|
asmlinkage __visible __trap_section void do_trap_break(struct pt_regs *regs)
|
|
|
|
{
|
|
|
|
if (user_mode(regs)) {
|
|
|
|
irqentry_enter_from_user_mode(regs);
|
|
|
|
|
|
|
|
handle_break(regs);
|
|
|
|
|
|
|
|
irqentry_exit_to_user_mode(regs);
|
|
|
|
} else {
|
|
|
|
irqentry_state_t state = irqentry_nmi_enter(regs);
|
|
|
|
|
|
|
|
handle_break(regs);
|
|
|
|
|
|
|
|
irqentry_nmi_exit(regs, state);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-09 06:37:51 -07:00
|
|
|
asmlinkage __visible __trap_section __no_stack_protector
|
|
|
|
void do_trap_ecall_u(struct pt_regs *regs)
|
2023-02-21 20:30:18 -07:00
|
|
|
{
|
|
|
|
if (user_mode(regs)) {
|
riscv: entry: set a0 = -ENOSYS only when syscall != -1
When we test seccomp with 6.4 kernel, we found errno has wrong value.
If we deny NETLINK_AUDIT with EAFNOSUPPORT, after f0bddf50586d, we will
get ENOSYS instead. We got same result with commit 9c2598d43510 ("riscv:
entry: Save a0 prior syscall_enter_from_user_mode()").
After analysing code, we think that regs->a0 = -ENOSYS should only be
executed when syscall != -1. In __seccomp_filter, when seccomp rejected
this syscall with specified errno, they will set a0 to return number as
syscall ABI, and then return -1. This return number is finally pass as
return number of syscall_enter_from_user_mode, and then is compared with
NR_syscalls after converted to ulong (so it will be ULONG_MAX). The
condition syscall < NR_syscalls will always be false, so regs->a0 = -ENOSYS
is always executed. It covered a0 set by seccomp, so we always get
ENOSYS when match seccomp RET_ERRNO rule.
Fixes: f0bddf50586d ("riscv: entry: Convert to generic entry")
Reported-by: Felix Yan <felixonmars@archlinux.org>
Co-developed-by: Ruizhe Pan <c141028@gmail.com>
Signed-off-by: Ruizhe Pan <c141028@gmail.com>
Co-developed-by: Shiqi Zhang <shiqi@isrc.iscas.ac.cn>
Signed-off-by: Shiqi Zhang <shiqi@isrc.iscas.ac.cn>
Signed-off-by: Celeste Liu <CoelacanthusHex@gmail.com>
Tested-by: Felix Yan <felixonmars@archlinux.org>
Tested-by: Emil Renner Berthing <emil.renner.berthing@canonical.com>
Reviewed-by: Björn Töpel <bjorn@rivosinc.com>
Reviewed-by: Guo Ren <guoren@kernel.org>
Link: https://lore.kernel.org/r/20230801141607.435192-1-CoelacanthusHex@gmail.com
Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
2023-08-01 07:15:16 -07:00
|
|
|
long syscall = regs->a7;
|
2023-02-21 20:30:18 -07:00
|
|
|
|
|
|
|
regs->epc += 4;
|
|
|
|
regs->orig_a0 = regs->a0;
|
2024-06-27 07:23:39 -07:00
|
|
|
regs->a0 = -ENOSYS;
|
2023-02-21 20:30:18 -07:00
|
|
|
|
2023-06-29 07:22:28 -07:00
|
|
|
riscv_v_vstate_discard(regs);
|
|
|
|
|
2023-04-02 23:52:07 -07:00
|
|
|
syscall = syscall_enter_from_user_mode(regs, syscall);
|
|
|
|
|
2023-11-09 06:37:51 -07:00
|
|
|
add_random_kstack_offset();
|
|
|
|
|
riscv: entry: set a0 = -ENOSYS only when syscall != -1
When we test seccomp with 6.4 kernel, we found errno has wrong value.
If we deny NETLINK_AUDIT with EAFNOSUPPORT, after f0bddf50586d, we will
get ENOSYS instead. We got same result with commit 9c2598d43510 ("riscv:
entry: Save a0 prior syscall_enter_from_user_mode()").
After analysing code, we think that regs->a0 = -ENOSYS should only be
executed when syscall != -1. In __seccomp_filter, when seccomp rejected
this syscall with specified errno, they will set a0 to return number as
syscall ABI, and then return -1. This return number is finally pass as
return number of syscall_enter_from_user_mode, and then is compared with
NR_syscalls after converted to ulong (so it will be ULONG_MAX). The
condition syscall < NR_syscalls will always be false, so regs->a0 = -ENOSYS
is always executed. It covered a0 set by seccomp, so we always get
ENOSYS when match seccomp RET_ERRNO rule.
Fixes: f0bddf50586d ("riscv: entry: Convert to generic entry")
Reported-by: Felix Yan <felixonmars@archlinux.org>
Co-developed-by: Ruizhe Pan <c141028@gmail.com>
Signed-off-by: Ruizhe Pan <c141028@gmail.com>
Co-developed-by: Shiqi Zhang <shiqi@isrc.iscas.ac.cn>
Signed-off-by: Shiqi Zhang <shiqi@isrc.iscas.ac.cn>
Signed-off-by: Celeste Liu <CoelacanthusHex@gmail.com>
Tested-by: Felix Yan <felixonmars@archlinux.org>
Tested-by: Emil Renner Berthing <emil.renner.berthing@canonical.com>
Reviewed-by: Björn Töpel <bjorn@rivosinc.com>
Reviewed-by: Guo Ren <guoren@kernel.org>
Link: https://lore.kernel.org/r/20230801141607.435192-1-CoelacanthusHex@gmail.com
Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
2023-08-01 07:15:16 -07:00
|
|
|
if (syscall >= 0 && syscall < NR_syscalls)
|
2023-02-21 20:30:18 -07:00
|
|
|
syscall_handler(regs, syscall);
|
2024-06-27 07:23:39 -07:00
|
|
|
|
2023-11-09 06:37:51 -07:00
|
|
|
/*
|
|
|
|
* Ultimately, this value will get limited by KSTACK_OFFSET_MAX(),
|
|
|
|
* so the maximum stack offset is 1k bytes (10 bits).
|
|
|
|
*
|
|
|
|
* The actual entropy will be further reduced by the compiler when
|
|
|
|
* applying stack alignment constraints: 16-byte (i.e. 4-bit) aligned
|
|
|
|
* for RV32I or RV64I.
|
|
|
|
*
|
|
|
|
* The resulting 6 bits of entropy is seen in SP[9:4].
|
|
|
|
*/
|
|
|
|
choose_random_kstack_offset(get_random_u16());
|
2023-02-21 20:30:18 -07:00
|
|
|
|
|
|
|
syscall_exit_to_user_mode(regs);
|
|
|
|
} else {
|
|
|
|
irqentry_state_t state = irqentry_nmi_enter(regs);
|
|
|
|
|
|
|
|
do_trap_error(regs, SIGILL, ILL_ILLTRP, regs->epc,
|
|
|
|
"Oops - environment call from U-mode");
|
|
|
|
|
|
|
|
irqentry_nmi_exit(regs, state);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_MMU
|
|
|
|
asmlinkage __visible noinstr void do_page_fault(struct pt_regs *regs)
|
|
|
|
{
|
|
|
|
irqentry_state_t state = irqentry_enter(regs);
|
|
|
|
|
|
|
|
handle_page_fault(regs);
|
|
|
|
|
|
|
|
local_irq_disable();
|
|
|
|
|
|
|
|
irqentry_exit(regs, state);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2023-06-13 18:30:16 -07:00
|
|
|
static void noinstr handle_riscv_irq(struct pt_regs *regs)
|
2023-02-21 20:30:18 -07:00
|
|
|
{
|
|
|
|
struct pt_regs *old_regs;
|
|
|
|
|
|
|
|
irq_enter_rcu();
|
|
|
|
old_regs = set_irq_regs(regs);
|
|
|
|
handle_arch_irq(regs);
|
|
|
|
set_irq_regs(old_regs);
|
|
|
|
irq_exit_rcu();
|
2023-06-13 18:30:16 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
asmlinkage void noinstr do_irq(struct pt_regs *regs)
|
|
|
|
{
|
|
|
|
irqentry_state_t state = irqentry_enter(regs);
|
2023-09-27 15:48:00 -07:00
|
|
|
|
|
|
|
if (IS_ENABLED(CONFIG_IRQ_STACKS) && on_thread_stack())
|
|
|
|
call_on_irq_stack(regs, handle_riscv_irq);
|
|
|
|
else
|
2023-06-13 18:30:16 -07:00
|
|
|
handle_riscv_irq(regs);
|
2023-02-21 20:30:18 -07:00
|
|
|
|
|
|
|
irqentry_exit(regs, state);
|
|
|
|
}
|
2017-07-10 18:00:26 -07:00
|
|
|
|
|
|
|
#ifdef CONFIG_GENERIC_BUG
|
|
|
|
int is_valid_bugaddr(unsigned long pc)
|
|
|
|
{
|
|
|
|
bug_insn_t insn;
|
|
|
|
|
2019-03-04 20:23:35 -07:00
|
|
|
if (pc < VMALLOC_START)
|
2017-07-10 18:00:26 -07:00
|
|
|
return 0;
|
2020-06-17 00:37:55 -07:00
|
|
|
if (get_kernel_nofault(insn, (bug_insn_t *)pc))
|
2017-07-10 18:00:26 -07:00
|
|
|
return 0;
|
2019-03-04 20:23:34 -07: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-10 18:00:26 -07: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-20 20:28:55 -07:00
|
|
|
#ifdef CONFIG_VMAP_STACK
|
2023-09-27 15:47:59 -07:00
|
|
|
DEFINE_PER_CPU(unsigned long [OVERFLOW_STACK_SIZE/sizeof(long)],
|
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-20 20:28:55 -07:00
|
|
|
overflow_stack)__aligned(16);
|
|
|
|
|
|
|
|
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
|