From d9f1f4dc11c70cf6215d5625ec54392d7d3f4110 Mon Sep 17 00:00:00 2001 From: John Baldwin Date: Tue, 9 Jul 2024 06:29:27 -0700 Subject: [PATCH 1/3] cheribsdtest: Test signal for invalid memory accesses in hybrid --- bin/cheribsdtest/arm64/cheribsdtest_md.h | 3 + bin/cheribsdtest/cheribsdtest_signal.c | 84 ++++++++++++++++++++++++ bin/cheribsdtest/riscv/cheribsdtest_md.h | 3 + 3 files changed, 90 insertions(+) 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 From 08f5572c83af7d2af4392f01e206f1675eec6867 Mon Sep 17 00:00:00 2001 From: John Baldwin Date: Tue, 9 Jul 2024 06:30:58 -0700 Subject: [PATCH 2/3] arm64: Raise SIGSEGV for bounds violations outside of UVA for hybrid --- sys/arm64/arm64/trap.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) 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 From 564aee157bee74e4065a024939d267246f02e09b Mon Sep 17 00:00:00 2001 From: John Baldwin Date: Tue, 9 Jul 2024 06:32:06 -0700 Subject: [PATCH 3/3] riscv: Raise SIGSEGV for bounds violations against PCC/DDC in hybrid The signal for the DDC case isn't fully correct since the instruction would need to be decoded to compute the effective address and access mode (read vs write). --- sys/riscv/riscv/trap.c | 43 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) 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));