mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 03:04:51 +01:00
riscv/traps: Introduce software check exception and uprobe handling
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>
This commit is contained in:
parent
8a9e22d2ca
commit
9d42fc28fc
4 changed files with 60 additions and 0 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue