summaryrefslogtreecommitdiff
path: root/arch/riscv/kernel/traps.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/riscv/kernel/traps.c')
-rw-r--r--arch/riscv/kernel/traps.c54
1 files changed, 54 insertions, 0 deletions
diff --git a/arch/riscv/kernel/traps.c b/arch/riscv/kernel/traps.c
index 47afea4ff1a8..5fb57fad188a 100644
--- a/arch/riscv/kernel/traps.c
+++ b/arch/riscv/kernel/traps.c
@@ -368,6 +368,60 @@ void do_trap_ecall_u(struct pt_regs *regs)
}
+#define CFI_TVAL_FCFI_CODE 2
+#define CFI_TVAL_BCFI_CODE 3
+/* handle cfi violations */
+bool handle_user_cfi_violation(struct pt_regs *regs)
+{
+ unsigned long tval = csr_read(CSR_TVAL);
+ bool is_fcfi = (tval == CFI_TVAL_FCFI_CODE && cpu_supports_indirect_br_lp_instr());
+ bool is_bcfi = (tval == CFI_TVAL_BCFI_CODE && cpu_supports_shadow_stack());
+
+ /*
+ * Handle uprobe event first. The probe point can be a valid target
+ * of indirect jumps or calls, in this case, forward cfi violation
+ * will be triggered instead of breakpoint exception. Clear ELP flag
+ * on sstatus image as well to avoid recurring fault.
+ */
+ if (is_fcfi && probe_breakpoint_handler(regs)) {
+ regs->status &= ~SR_ELP;
+ return true;
+ }
+
+ if (is_fcfi || is_bcfi) {
+ do_trap_error(regs, SIGSEGV, SEGV_CPERR, regs->epc,
+ "Oops - control flow violation");
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * software check exception is defined with risc-v cfi spec. Software check
+ * exception is raised when:
+ * a) An indirect branch doesn't land on 4 byte aligned PC or `lpad`
+ * instruction or `label` value programmed in `lpad` instr doesn't
+ * match with value setup in `x7`. reported code in `xtval` is 2.
+ * b) `sspopchk` instruction finds a mismatch between top of shadow stack (ssp)
+ * and x1/x5. reported code in `xtval` is 3.
+ */
+asmlinkage __visible __trap_section void do_trap_software_check(struct pt_regs *regs)
+{
+ if (user_mode(regs)) {
+ irqentry_enter_from_user_mode(regs);
+
+ /* not a cfi violation, then merge into flow of unknown trap handler */
+ if (!handle_user_cfi_violation(regs))
+ do_trap_unknown(regs);
+
+ irqentry_exit_to_user_mode(regs);
+ } else {
+ /* sw check exception coming from kernel is a bug in kernel */
+ die(regs, "Kernel BUG");
+ }
+}
+
#ifdef CONFIG_MMU
asmlinkage __visible noinstr void do_page_fault(struct pt_regs *regs)
{