diff --git a/bin/cheribsdtest/arm64/cheribsdtest_md.h b/bin/cheribsdtest/arm64/cheribsdtest_md.h index 96232527bf70..bf2a1346af01 100644 --- a/bin/cheribsdtest/arm64/cheribsdtest_md.h +++ b/bin/cheribsdtest/arm64/cheribsdtest_md.h @@ -40,6 +40,9 @@ #define TRAPNO_STORE_CAP_PF EXCP_DATA_ABORT_L #define TRAPNO_LOAD_STORE EXCP_DATA_ABORT_L +#define TRAPNO_LOAD_PF EXCP_DATA_ABORT_L +#define TRAPNO_STORE_PF EXCP_DATA_ABORT_L +#define TRAPNO_EXEC_PF EXCP_INSN_ABORT_L #define CHERI_SEAL_VIOLATION_EXCEPTION 0 diff --git a/bin/cheribsdtest/cheribsdtest_signal.c b/bin/cheribsdtest/cheribsdtest_signal.c index 653efab5f836..b5cd47ee670c 100644 --- a/bin/cheribsdtest/cheribsdtest_signal.c +++ b/bin/cheribsdtest/cheribsdtest_signal.c @@ -296,3 +296,87 @@ CHERIBSDTEST(signal_returncap, cheribsdtest_success(); } #endif + +#ifndef __CHERI_PURE_CAPABILITY__ +/* + * Ensure that invalid addresses still raise SIGSEGV (rather than + * SIGPROT) for hybrid mode. + */ +CHERIBSDTEST(null_pointer_load_sigsegv, + "Check that loading from NULL raises SIGSEGV", + .ct_flags = CT_FLAG_SIGNAL | CT_FLAG_SI_CODE | CT_FLAG_SI_TRAPNO, + .ct_signum = SIGSEGV, + .ct_si_code = SEGV_MAPERR, + .ct_si_trapno = TRAPNO_LOAD_PF) +{ + volatile char *p = (void *)(uintptr_t)1; + + (void)*p; + cheribsdtest_failure_errx("Unexpected load from NULL pointer"); +} + +CHERIBSDTEST(null_pointer_store_sigsegv, + "Check that storing to NULL raises SIGSEGV", + .ct_flags = CT_FLAG_SIGNAL | CT_FLAG_SI_CODE | CT_FLAG_SI_TRAPNO, + .ct_signum = SIGSEGV, + .ct_si_code = SEGV_MAPERR, + .ct_si_trapno = TRAPNO_STORE_PF) +{ + char *p = (void *)(uintptr_t)1; + + *p = 1; + cheribsdtest_failure_errx("Unexpected store to NULL pointer"); +} + +CHERIBSDTEST(null_pointer_exec_sigsegv, + "Check that branching to NULL raises SIGSEGV", + .ct_flags = CT_FLAG_SIGNAL | CT_FLAG_SI_CODE | CT_FLAG_SI_TRAPNO, + .ct_signum = SIGSEGV, + .ct_si_code = SEGV_MAPERR, + .ct_si_trapno = TRAPNO_EXEC_PF) +{ + void (*p)(void) = (void *)(uintptr_t)1; + + p(); + cheribsdtest_failure_errx("Unexpected branch to NULL pointer"); +} + +CHERIBSDTEST(kernel_pointer_load_sigsegv, + "Check that loading from a kernel address raises SIGSEGV", + .ct_flags = CT_FLAG_SIGNAL | CT_FLAG_SI_CODE | CT_FLAG_SI_TRAPNO, + .ct_signum = SIGSEGV, + .ct_si_code = SEGV_MAPERR, + .ct_si_trapno = TRAPNO_LOAD_PF) +{ + volatile char *p = (void *)(uintptr_t)VM_MIN_KERNEL_ADDRESS; + + (void)*p; + cheribsdtest_failure_errx("Unexpected load from kernel address"); +} + +CHERIBSDTEST(kernel_pointer_store_sigsegv, + "Check that storing to a kernel address raises SIGSEGV", + .ct_flags = CT_FLAG_SIGNAL | CT_FLAG_SI_CODE | CT_FLAG_SI_TRAPNO, + .ct_signum = SIGSEGV, + .ct_si_code = SEGV_MAPERR, + .ct_si_trapno = TRAPNO_STORE_PF) +{ + char *p = (void *)(uintptr_t)VM_MIN_KERNEL_ADDRESS; + + *p = 1; + cheribsdtest_failure_errx("Unexpected store to kernel address"); +} + +CHERIBSDTEST(kernel_pointer_exec_sigsegv, + "Check that branching to a kernel address raises SIGSEGV", + .ct_flags = CT_FLAG_SIGNAL | CT_FLAG_SI_CODE | CT_FLAG_SI_TRAPNO, + .ct_signum = SIGSEGV, + .ct_si_code = SEGV_MAPERR, + .ct_si_trapno = TRAPNO_EXEC_PF) +{ + void (*p)(void) = (void *)(uintptr_t)VM_MIN_KERNEL_ADDRESS; + + p(); + cheribsdtest_failure_errx("Unexpected branch to kernel address"); +} +#endif diff --git a/bin/cheribsdtest/riscv/cheribsdtest_md.h b/bin/cheribsdtest/riscv/cheribsdtest_md.h index 763b4c22e60e..4e21fd9bbac8 100644 --- a/bin/cheribsdtest/riscv/cheribsdtest_md.h +++ b/bin/cheribsdtest/riscv/cheribsdtest_md.h @@ -41,6 +41,9 @@ #define TRAPNO_CHERI (SCAUSE_CHERI) #define TRAPNO_STORE_CAP_PF (SCAUSE_STORE_AMO_CAP_PAGE_FAULT) #define TRAPNO_LOAD_STORE (SCAUSE_CHERI) +#define TRAPNO_LOAD_PF (SCAUSE_LOAD_PAGE_FAULT) +#define TRAPNO_STORE_PF (SCAUSE_STORE_PAGE_FAULT) +#define TRAPNO_EXEC_PF (SCAUSE_INST_PAGE_FAULT) #define CHERI_SEAL_VIOLATION_EXCEPTION 1 diff --git a/sys/arm64/arm64/trap.c b/sys/arm64/arm64/trap.c index a3283a4c2ab3..53658aed48ae 100644 --- a/sys/arm64/arm64/trap.c +++ b/sys/arm64/arm64/trap.c @@ -346,8 +346,20 @@ cap_abort(struct thread *td, struct trapframe *frame, uint64_t esr, printf(" esr: %.8lx\n", esr); } - call_trapsignal(td, SIGPROT, cheri_esr_to_sicode(esr), - (void * __capability)frame->tf_elr, ESR_ELx_EXCEPTION(esr)); + /* + * User accesses to invalid addresses in a compat64 process + * raise SIGSEGV under a non-CHERI kernel via a non-capability + * data abort. With CHERI however, those accesses can raise a + * capability abort if they are outside the bounds of the user + * DDC. Map those accesses to SIGSEGV instead of SIGPROT. + */ + if (!SV_PROC_FLAG(td->td_proc, SV_CHERI) && + far > CHERI_CAP_USER_DATA_BASE + CHERI_CAP_USER_DATA_LENGTH) + call_trapsignal(td, SIGSEGV, SEGV_MAPERR, + (void * __capability)(uintcap_t)far, ESR_ELx_EXCEPTION(esr)); + else + call_trapsignal(td, SIGPROT, cheri_esr_to_sicode(esr), + (void * __capability)frame->tf_elr, ESR_ELx_EXCEPTION(esr)); userret(td, frame); } #endif diff --git a/sys/riscv/riscv/trap.c b/sys/riscv/riscv/trap.c index 8daba5a1e63e..e67b4eb6fa58 100644 --- a/sys/riscv/riscv/trap.c +++ b/sys/riscv/riscv/trap.c @@ -653,6 +653,49 @@ do_trap_user(struct trapframe *frame) case SCAUSE_CHERI: if (log_user_cheri_exceptions) dump_cheri_exception(frame); + + /* + * User accesses to invalid addresses in a compat64 + * process raise SIGSEGV under a non-CHERI kernel via + * a page fault exception. With CHERI however, those + * accesses can raise a capability abort if they are + * outside the bounds of the user DDC. Map those + * accesses to SIGSEGV instead of SIGPROT. + */ + if (!SV_PROC_FLAG(td->td_proc, SV_CHERI) && + TVAL_CAP_CAUSE(frame->tf_stval) == CHERI_EXCCODE_LENGTH) { + if (TVAL_CAP_IDX(frame->tf_stval) == 32 /* PCC */ && + cheri_getbase(frame->tf_sepc) == + CHERI_CAP_USER_DATA_BASE && + cheri_getlen(frame->tf_sepc) == + CHERI_CAP_USER_DATA_LENGTH) { + call_trapsignal(td, SIGSEGV, SEGV_MAPERR, + (ptraddr_t)frame->tf_sepc, + SCAUSE_INST_PAGE_FAULT, 0); + userret(td, frame); + break; + } + + /* + * To fully mimic SIGSEGV, this would need to + * decode the instruction to compute the + * effective faulting address and access type + * (R/W) to determine the non-CHERI exception + * that would have been raised. + */ + if (TVAL_CAP_IDX(frame->tf_stval) == 33 /* DDC */ && + cheri_getbase(frame->tf_ddc) == + CHERI_CAP_USER_DATA_BASE && + cheri_getlen(frame->tf_ddc) == + CHERI_CAP_USER_DATA_LENGTH) { + call_trapsignal(td, SIGSEGV, SEGV_MAPERR, + 0 /* XXX */, + SCAUSE_LOAD_PAGE_FAULT /* XXX */, 0); + userret(td, frame); + break; + } + } + call_trapsignal(td, SIGPROT, cheri_stval_to_sicode(frame->tf_stval), frame->tf_sepc, exception, TVAL_CAP_IDX(frame->tf_stval));