]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
riscv/traps: Introduce software check exception and uprobe handling
authorDeepak Gupta <debug@rivosinc.com>
Mon, 26 Jan 2026 04:09:54 +0000 (21:09 -0700)
committerPaul Walmsley <pjw@kernel.org>
Thu, 29 Jan 2026 09:38:40 +0000 (02:38 -0700)
The Zicfiss and Zicfilp extensions introduce a new exception, the
'software check exception', in the privileged ISA, with cause code =
18. This patch implements support for software check exceptions.

Additionally, the patch implements a CFI violation handler which
checks the code in the xtval register. If xtval=2, the software check
exception happened because of an indirect branch that didn't land on a
4 byte aligned PC or on a 'lpad' instruction, or the label value
embedded in 'lpad' didn't match the label value set in the x7
register. If xtval=3, the software check exception happened due to a
mismatch between the link register (x1 or x5) and the top of shadow
stack (on execution of `sspopchk`).

In case of a CFI violation, SIGSEGV is raised with code=SEGV_CPERR.
SEGV_CPERR was introduced by the x86 shadow stack patches.

To keep uprobes working, handle the uprobe event first before
reporting the CFI violation in the software check exception
handler. This is because, when the landing pad is activated, if the
uprobe point is set at the lpad instruction at the beginning of a
function, the system triggers a software check exception instead of an
ebreak exception due to the exception priority.  This would prevent
uprobe from working.

Reviewed-by: Zong Li <zong.li@sifive.com>
Co-developed-by: Zong Li <zong.li@sifive.com>
Signed-off-by: Zong Li <zong.li@sifive.com>
Signed-off-by: Deepak Gupta <debug@rivosinc.com>
Tested-by: Andreas Korb <andreas.korb@aisec.fraunhofer.de> # QEMU, custom CVA6
Tested-by: Valentin Haudiquet <valentin.haudiquet@canonical.com>
Link: https://patch.msgid.link/20251112-v5_user_cfi_series-v23-15-b55691eacf4f@rivosinc.com
[pjw@kernel.org: cleaned up the patch description]
Signed-off-by: Paul Walmsley <pjw@kernel.org>
arch/riscv/include/asm/asm-prototypes.h
arch/riscv/include/asm/entry-common.h
arch/riscv/kernel/entry.S
arch/riscv/kernel/traps.c

index a9988bf21ec8e6e68ced81a4b32a76434c137629..41ec5cdec3673cc410fedf2542b6a6ffd2da5e98 100644 (file)
@@ -51,6 +51,7 @@ DECLARE_DO_ERROR_INFO(do_trap_ecall_u);
 DECLARE_DO_ERROR_INFO(do_trap_ecall_s);
 DECLARE_DO_ERROR_INFO(do_trap_ecall_m);
 DECLARE_DO_ERROR_INFO(do_trap_break);
+DECLARE_DO_ERROR_INFO(do_trap_software_check);
 
 asmlinkage void ret_from_fork_kernel(void *fn_arg, int (*fn)(void *), struct pt_regs *regs);
 asmlinkage void ret_from_fork_user(struct pt_regs *regs);
index b28ccc6cdeea49fc32290caa02361def08600bf4..34ed149af5d1b4335ea212255fce29d4a6af353a 100644 (file)
@@ -40,4 +40,6 @@ static inline int handle_misaligned_store(struct pt_regs *regs)
 }
 #endif
 
+bool handle_user_cfi_violation(struct pt_regs *regs);
+
 #endif /* _ASM_RISCV_ENTRY_COMMON_H */
index 2d0a9a3ce231f238c8ab736d36222bf65b179112..60eb221296a604694c5f936d0c23c637dc44298e 100644 (file)
@@ -495,6 +495,9 @@ SYM_DATA_START_LOCAL(excp_vect_table)
        RISCV_PTR do_page_fault   /* load page fault */
        RISCV_PTR do_trap_unknown
        RISCV_PTR do_page_fault   /* store page fault */
+       RISCV_PTR do_trap_unknown /* cause=16 */
+       RISCV_PTR do_trap_unknown /* cause=17 */
+       RISCV_PTR do_trap_software_check /* cause=18 is sw check exception */
 SYM_DATA_END_LABEL(excp_vect_table, SYM_L_LOCAL, excp_vect_table_end)
 
 #ifndef CONFIG_MMU
index 47afea4ff1a8d224adbaa99208c7f5e4ffecd526..5fb57fad188a9a089b0d1415c3a417306ffb0a41 100644 (file)
@@ -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)
 {