From 128f6a5f004f8c2782ffcc43568008ca4fdfdbb5 Mon Sep 17 00:00:00 2001 From: Dapeng Gao Date: Thu, 25 Jul 2024 15:18:56 +0100 Subject: [PATCH 01/12] c18n: Remove unnecessary check for purecap ABI CHERI_LIB_C18N is only ever defined under the purecap ABI, so there is no need to separately check for the purecap ABI. --- lib/libc/aarch64/gen/_setjmp.S | 10 ++--- lib/libc/aarch64/gen/setjmp.S | 12 +++--- lib/libc/gen/posix_spawn.c | 2 +- lib/libc/include/libc_private.h | 2 +- lib/libc/sys/sigaction.c | 2 +- lib/libsys/interposing_table.c | 2 +- lib/libthr/thread/thr_create.c | 8 ++-- lib/libthr/thread/thr_rtld.c | 4 +- lib/libthr/thread/thr_sig.c | 16 +++---- libexec/rtld-elf/aarch64/reloc.c | 12 +++--- libexec/rtld-elf/aarch64/rtld_start.S | 6 +-- libexec/rtld-elf/cheri/cheri_reloc.h | 2 +- libexec/rtld-elf/map_object.c | 4 +- libexec/rtld-elf/rtld-libc/rtld_libc.h | 2 +- libexec/rtld-elf/rtld.c | 58 +++++++++++++------------- libexec/rtld-elf/rtld.h | 2 +- libexec/rtld-elf/rtld_lock.c | 10 ++--- libexec/rtld-elf/rtld_lock.h | 2 +- sys/sys/link_elf.h | 2 +- 19 files changed, 79 insertions(+), 79 deletions(-) diff --git a/lib/libc/aarch64/gen/_setjmp.S b/lib/libc/aarch64/gen/_setjmp.S index 1336dc154c6e..66a239b563a3 100644 --- a/lib/libc/aarch64/gen/_setjmp.S +++ b/lib/libc/aarch64/gen/_setjmp.S @@ -55,11 +55,11 @@ ENTRY(_setjmp) #endif /* Return value */ -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N mov c1, c0 #endif mov x0, #0 -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N /* * Tail-call to save Executive mode state */ @@ -81,7 +81,7 @@ ENTRY(_longjmp) /* Restore the stack pointer */ ldr REG(8), [REG(0)], #(REG_WIDTH) -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N mov c2, c8 #else mov REGN(sp), REG(8) @@ -104,12 +104,12 @@ ENTRY(_longjmp) #endif /* Load the return value */ -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N mov c3, c0 #endif cmp x1, #0 csinc x0, x1, xzr, ne -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N /* * Tail-call to restore Executive mode state */ diff --git a/lib/libc/aarch64/gen/setjmp.S b/lib/libc/aarch64/gen/setjmp.S index 3df8dde53469..164199ca695a 100644 --- a/lib/libc/aarch64/gen/setjmp.S +++ b/lib/libc/aarch64/gen/setjmp.S @@ -32,7 +32,7 @@ #include #include -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N .weak _rtld_setjmp .weak _rtld_longjmp #endif @@ -70,11 +70,11 @@ ENTRY(setjmp) stp d14, d15, [REG(0)], #16 /* Return value */ -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N mov c1, c0 #endif mov x0, #0 -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N /* * Tail-call to save Executive mode state */ @@ -110,7 +110,7 @@ ENTRY(longjmp) /* Restore the stack pointer */ ldr REG(8), [REG(0)], #(REG_WIDTH) -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N mov c2, c8 #else mov REGN(sp), REG(8) @@ -131,12 +131,12 @@ ENTRY(longjmp) ldp d14, d15, [REG(0)], #16 /* Load the return value */ -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N mov c3, c0 #endif cmp x1, #0 csinc x0, x1, xzr, ne -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N /* * Tail-call to restore Executive mode state */ diff --git a/lib/libc/gen/posix_spawn.c b/lib/libc/gen/posix_spawn.c index 1e91d99d03b4..48df35b0fbd5 100644 --- a/lib/libc/gen/posix_spawn.c +++ b/lib/libc/gen/posix_spawn.c @@ -341,7 +341,7 @@ do_posix_spawn(pid_t *pid, const char *path, p = rfork_thread(RFSPAWN, stack + stacksz, _posix_spawn_thr, &psa); free(stack); #else -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N p = __sys_rfork(RFSPAWN); #else p = rfork(RFSPAWN); diff --git a/lib/libc/include/libc_private.h b/lib/libc/include/libc_private.h index 2ef540f2fc0c..6a000c7fa6b9 100644 --- a/lib/libc/include/libc_private.h +++ b/lib/libc/include/libc_private.h @@ -394,7 +394,7 @@ struct __nl_cat_d *__catopen_l(const char *name, int type, int __strerror_rl(int errnum, char *strerrbuf, size_t buflen, struct _xlocale *locale); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N __pid_t __sys_rfork(int); int sigaction_c18n(int, const struct sigaction *, struct sigaction *); #endif diff --git a/lib/libc/sys/sigaction.c b/lib/libc/sys/sigaction.c index 28918f6e726c..568dc7dc4526 100644 --- a/lib/libc/sys/sigaction.c +++ b/lib/libc/sys/sigaction.c @@ -35,7 +35,7 @@ __weak_reference(__sys_sigaction, __sigaction); __weak_reference(sigaction, __libc_sigaction); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N /* * This weak symbol will always be resolved at runtime. */ diff --git a/lib/libsys/interposing_table.c b/lib/libsys/interposing_table.c index 5c6d81d6ff51..989aa6f75652 100644 --- a/lib/libsys/interposing_table.c +++ b/lib/libsys/interposing_table.c @@ -56,7 +56,7 @@ static interpos_func_t __libsys_interposing[INTERPOS_MAX] = { SLOT(sendmsg, __sys_sendmsg), SLOT(sendto, __sys_sendto), SLOT(setcontext, __sys_setcontext), -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N SLOT(sigaction, sigaction_c18n), #else SLOT(sigaction, __sys_sigaction), diff --git a/lib/libthr/thread/thr_create.c b/lib/libthr/thread/thr_create.c index bf5cfe3530fb..9c41a88c1755 100644 --- a/lib/libthr/thread/thr_create.c +++ b/lib/libthr/thread/thr_create.c @@ -56,7 +56,7 @@ static int create_stack(struct pthread_attr *pattr); static void thread_start(struct pthread *curthread) __used; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N #pragma weak _thread_start = thread_start /* @@ -151,7 +151,7 @@ _pthread_create(pthread_t * __restrict thread, new_thread->flags = THR_FLAGS_NEED_SUSPEND; create_suspended = 1; } else { -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N /* * c18n: Always block all signals when creating a new thread to * allow RTLD to set up the environment to handle signals. @@ -187,7 +187,7 @@ _pthread_create(pthread_t * __restrict thread, locked = 1; } else locked = 0; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N param.start_func = (void (*)(void *)) _rtld_thread_start; #else param.start_func = (void (*)(void *)) thread_start; @@ -298,7 +298,7 @@ thread_start(struct pthread *curthread) sigset_t set; bool restore_sigmask; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N /* * At this point, curthread->tcb contains a fake wrapper TCB created by * RTLD when the thread was created. The real TCB has now been installed diff --git a/lib/libthr/thread/thr_rtld.c b/lib/libthr/thread/thr_rtld.c index 21c643c8dcab..f94622c714f2 100644 --- a/lib/libthr/thread/thr_rtld.c +++ b/lib/libthr/thread/thr_rtld.c @@ -50,7 +50,7 @@ static void _thr_rtld_rlock_acquire(void *); static int _thr_rtld_set_flag(int); static void _thr_rtld_wlock_acquire(void *); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N void _thread_start(struct pthread *); void _thr_sighandler(int, siginfo_t *, void *); @@ -291,7 +291,7 @@ _thr_rtld_init(void) /* mask signals, also force to resolve __sys_sigprocmask PLT */ _thr_signal_block(curthread); _rtld_thread_init(&li); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N _rtld_thread_start_init(_thread_start); _rtld_sighandler_init(_thr_sighandler); #endif diff --git a/lib/libthr/thread/thr_sig.c b/lib/libthr/thread/thr_sig.c index 685561e2b07e..c77d7bc04da0 100644 --- a/lib/libthr/thread/thr_sig.c +++ b/lib/libthr/thread/thr_sig.c @@ -69,7 +69,7 @@ static void handle_signal(struct sigaction *, int, siginfo_t *, ucontext_t *); static void check_deferred_signal(struct pthread *); static void check_suspend(struct pthread *); static void check_cancel(struct pthread *curthread, ucontext_t *ucp); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N #pragma weak _thr_sighandler = thr_sighandler /* @@ -296,7 +296,7 @@ handle_signal(struct sigaction *actp, int sig, siginfo_t *info, ucontext_t *ucp) if (!cancel_async) curthread->cancel_enable = 0; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N (void)sigfunc; #else /* restore correct mask before calling user handler */ @@ -313,7 +313,7 @@ handle_signal(struct sigaction *actp, int sig, siginfo_t *info, ucontext_t *ucp) * so after setjmps() returns once more, the user code may need to * re-set cancel_enable flag by calling pthread_setcancelstate(). */ -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N _rtld_siginvoke(sig, info, ucp, actp); #else if ((actp->sa_flags & SA_SIGINFO) != 0) { @@ -336,7 +336,7 @@ handle_signal(struct sigaction *actp, int sig, siginfo_t *info, ucontext_t *ucp) /* reschedule cancellation */ check_cancel(curthread, &uc2); errno = err; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N /* * Calling sigreturn outside of sigcode does not work with * compartmentalisation. Hence we set the user context and let the @@ -432,7 +432,7 @@ check_deferred_signal(struct pthread *curthread) /* remove signal */ curthread->deferred_siginfo.si_signo = 0; handle_signal(&act, info.si_signo, &info, uc); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N setcontext(uc); #endif } @@ -500,7 +500,7 @@ _thr_signal_init(int dlopened) for (sig = 1; sig <= _SIG_MAXSIG; sig++) { if (sig == SIGCANCEL) continue; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N error = _rtld_sigaction(sig, NULL, &oact); #else error = __sys_sigaction(sig, NULL, &oact); @@ -514,7 +514,7 @@ _thr_signal_init(int dlopened) remove_thr_signals(&usa->sigact.sa_mask); nact.sa_flags &= ~SA_NODEFER; nact.sa_flags |= SA_SIGINFO; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N /* XXX: Ignore sigaltstack for now */ nact.sa_flags &= ~SA_ONSTACK; nact.sa_sigaction = _rtld_sighandler; @@ -650,7 +650,7 @@ __thr_sigaction(int sig, const struct sigaction *act, struct sigaction *oact) remove_thr_signals(&usa->sigact.sa_mask); newact.sa_flags &= ~SA_NODEFER; newact.sa_flags |= SA_SIGINFO; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N /* XXX: Ignore sigaltstack for now */ newact.sa_flags &= ~SA_ONSTACK; newact.sa_sigaction = _rtld_sighandler; diff --git a/libexec/rtld-elf/aarch64/reloc.c b/libexec/rtld-elf/aarch64/reloc.c index 0bd8f75672ef..b18322a8e56a 100644 --- a/libexec/rtld-elf/aarch64/reloc.c +++ b/libexec/rtld-elf/aarch64/reloc.c @@ -38,7 +38,7 @@ #include "debug.h" #include "rtld.h" -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N #include "rtld_c18n.h" #endif #include "rtld_printf.h" @@ -112,14 +112,14 @@ init_pltgot(Obj_Entry *obj) { if (obj->pltgot != NULL) { -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N if (C18N_ENABLED) obj->pltgot[1] = (uintptr_t)cheri_seal(obj, sealer_pltgot); else #endif obj->pltgot[1] = (uintptr_t)obj; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N if (C18N_ENABLED) obj->pltgot[2] = (uintptr_t)&_rtld_bind_start_c18n; else @@ -510,7 +510,7 @@ reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) continue; } target = (uintptr_t)make_function_pointer(def, defobj); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N target = (uintptr_t)tramp_intern(obj, &(struct tramp_data) { .target = (void *)target, .defobj = defobj, @@ -572,7 +572,7 @@ reloc_iresolve_one(Obj_Entry *obj, const Elf_Rela *rela, ptr = (uintptr_t)(obj->relocbase + rela->r_addend); #endif lock_release(rtld_bind_lock, lockstate); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N ptr = (uintptr_t)tramp_intern(NULL, &(struct tramp_data) { .target = (void *)ptr, .defobj = obj, @@ -663,7 +663,7 @@ reloc_gnu_ifunc(Obj_Entry *obj, int flags, continue; lock_release(rtld_bind_lock, lockstate); target = (uintptr_t)rtld_resolve_ifunc(defobj, def); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N target = (uintptr_t)tramp_intern(obj, &(struct tramp_data) { .target = (void *)target, .defobj = defobj, diff --git a/libexec/rtld-elf/aarch64/rtld_start.S b/libexec/rtld-elf/aarch64/rtld_start.S index fab9b7bff62b..4d839d39eb16 100644 --- a/libexec/rtld-elf/aarch64/rtld_start.S +++ b/libexec/rtld-elf/aarch64/rtld_start.S @@ -119,7 +119,7 @@ END(.rtld_start) * x17 = &_rtld_bind_start */ ENTRY(C18N_SYM(_rtld_bind_start)) -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N /* * Get the caller's current stack top. */ @@ -205,7 +205,7 @@ ENTRY(C18N_SYM(_rtld_bind_start)) /* Restore frame pointer */ ldp PTR(29), PTR(zr), [PTRN(sp)], #(PTR_WIDTH * 2) -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N /* * Load caller's old stack top. */ @@ -246,7 +246,7 @@ ENTRY(C18N_SYM(_rtld_bind_start)) /* Call into the correct function */ #if defined(__ARM_MORELLO_PURECAP_BENCHMARK_ABI) br x16 -#elif defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#elif defined(CHERI_LIB_C18N) brr PTR(16) #else br PTR(16) diff --git a/libexec/rtld-elf/cheri/cheri_reloc.h b/libexec/rtld-elf/cheri/cheri_reloc.h index 1952eec1fd34..950d80fabb86 100644 --- a/libexec/rtld-elf/cheri/cheri_reloc.h +++ b/libexec/rtld-elf/cheri/cheri_reloc.h @@ -35,7 +35,7 @@ #include "debug.h" #include "rtld.h" -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N #include "rtld_c18n.h" #endif diff --git a/libexec/rtld-elf/map_object.c b/libexec/rtld-elf/map_object.c index dae71191a316..3865d872c374 100644 --- a/libexec/rtld-elf/map_object.c +++ b/libexec/rtld-elf/map_object.c @@ -51,7 +51,7 @@ #include "debug.h" #include "rtld.h" -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N #include "rtld_c18n.h" #endif @@ -278,7 +278,7 @@ map_object(int fd, const char *path, const struct stat *sb, const char* main_pat base_addr, mapsize, PROT_NONE | PROT_MAX(_PROT_ALL), base_flags); mapbase = mmap(base_addr, mapsize, PROT_NONE | PROT_MAX(_PROT_ALL), base_flags, -1, 0); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N mapbase = cheri_clearperm(mapbase, c18n_code_perm_clear); #endif if (mapbase == MAP_FAILED) { diff --git a/libexec/rtld-elf/rtld-libc/rtld_libc.h b/libexec/rtld-elf/rtld-libc/rtld_libc.h index 2132769c1d3e..7b1891aedf30 100644 --- a/libexec/rtld-elf/rtld-libc/rtld_libc.h +++ b/libexec/rtld-elf/rtld-libc/rtld_libc.h @@ -51,7 +51,7 @@ int __sys___getcwd(char *, size_t); int __sys_sigprocmask(int, const sigset_t *, sigset_t *); int __sys_thr_kill(long, int); int __sys_thr_self(long *); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N void __sys_thr_exit(long *); struct sigaction; int __sys_sigaction(int, const struct sigaction *, struct sigaction *); diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c index f831e683f651..37d790bb6b74 100644 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -68,7 +68,7 @@ #include "rtld_utrace.h" #include "notes.h" #include "rtld_libc.h" -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N #include "rtld_c18n.h" #endif @@ -396,7 +396,7 @@ enum { LD_SHOW_AUXV, LD_STATIC_TLS_EXTRA, LD_SKIP_INIT_FUNCS, -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N LD_UTRACE_COMPARTMENT, LD_COMPARTMENT_ENABLE, LD_COMPARTMENT_DISABLE, @@ -446,7 +446,7 @@ static struct ld_env_var_desc ld_env_vars[] = { LD_ENV_DESC(SHOW_AUXV, false), LD_ENV_DESC(STATIC_TLS_EXTRA, false), LD_ENV_DESC(SKIP_INIT_FUNCS, true), -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N LD_ENV_DESC(UTRACE_COMPARTMENT, false), LD_ENV_DESC(COMPARTMENT_ENABLE, false), LD_ENV_DESC(COMPARTMENT_DISABLE, false), @@ -682,7 +682,7 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) if (aux_info[AT_BSDFLAGS] != NULL) { if ((aux_info[AT_BSDFLAGS]->a_un.a_val & ELF_BSDF_SIGFASTBLK) != 0) ld_fast_sigblock = true; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N if ((aux_info[AT_BSDFLAGS]->a_un.a_val & ELF_BSDF_CHERI_C18N) != 0) ld_compartment_enable = true; #endif @@ -854,7 +854,7 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) if (!ld_tracing) ld_tracing = ld_get_env_var(LD_TRACE_LOADED_OBJECTS); ld_utrace = ld_get_env_var(LD_UTRACE); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N ld_compartment_utrace = ld_get_env_var(LD_UTRACE_COMPARTMENT); ld_compartment_policy = ld_get_env_var(LD_COMPARTMENT_POLICY); ld_compartment_overhead = ld_get_env_var(LD_COMPARTMENT_OVERHEAD); @@ -921,7 +921,7 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) assert(aux_info[AT_PHENT]->a_un.a_val == sizeof(Elf_Phdr)); assert(aux_info[AT_ENTRY] != NULL); imgentry = (dlfunc_t) aux_info[AT_ENTRY]->a_un.a_ptr; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N imgentry = (dlfunc_t) cheri_clearperm(imgentry, c18n_code_perm_clear); #endif dbg("Values from kernel:\n\tAT_PHDR=" PTR_FMT "\n" @@ -988,7 +988,7 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) linkmap_add(obj_main); linkmap_add(&obj_rtld); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N if (C18N_ENABLED) { c18n_init(&obj_rtld, aux_info); @@ -1064,7 +1064,7 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) exit (0); } -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N if (C18N_ENABLED) c18n_init2(&obj_rtld); #endif @@ -1183,7 +1183,7 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) /* Return the exit procedure and the program entry point. */ if (rtld_exit_ptr == NULL) { rtld_exit_ptr = make_rtld_function_pointer(rtld_exit); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N rtld_exit_ptr = tramp_intern(NULL, &(struct tramp_data) { .target = rtld_exit_ptr, .defobj = &obj_rtld, @@ -1197,7 +1197,7 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) *exit_proc = rtld_exit_ptr; *objp = obj_main; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N return ((func_ptr_type)tramp_intern(NULL, &(struct tramp_data) { .target = cheri_sealentry(obj_main->entry), .defobj = obj_main, @@ -1218,7 +1218,7 @@ rtld_resolve_ifunc(const Obj_Entry *obj, const Elf_Sym *def) uintptr_t target; ptr = (void *)make_function_pointer(def, obj); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N ptr = tramp_intern(NULL, &(struct tramp_data) { .target = ptr, .defobj = obj, @@ -1240,7 +1240,7 @@ _rtld_bind(Obj_Entry *obj, Elf_Size reloff) uintptr_t *where; uintptr_t target; RtldLockState lockstate; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N struct trusted_frame *tf; if (C18N_ENABLED) { @@ -1295,7 +1295,7 @@ _rtld_bind(Obj_Entry *obj, Elf_Size reloff) */ target = reloc_jmpslot(where, target, defobj, obj, rel); lock_release(rtld_bind_lock, &lockstate); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N if (C18N_ENABLED) tf = pop_dummy_rtld_trusted_frame(tf); #endif @@ -1572,7 +1572,7 @@ digest_dynamic1(Obj_Entry *obj, int early, const Elf_Dyn **dyn_rpath, assert(dynp->d_un.d_val == sizeof(Elf_Sym)); break; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N case DT_CHERI_C18N_SIG: if (ld_compartment_sig != NULL) obj->sigtab = (const struct func_sig *) @@ -3386,7 +3386,7 @@ Obj_Entry * obj_from_addr(const void *addr) { Obj_Entry *obj; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N struct tramp_header *header; if (C18N_ENABLED) { @@ -3555,7 +3555,7 @@ objlist_call_init(Objlist *list, RtldLockState *lockstate) lock_release(rtld_bind_lock, lockstate); if (reg != NULL) { func_ptr_type exit_ptr = make_rtld_function_pointer(rtld_exit); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N exit_ptr = tramp_intern(NULL, &(struct tramp_data) { .target = exit_ptr, .defobj = &obj_rtld, @@ -3568,7 +3568,7 @@ objlist_call_init(Objlist *list, RtldLockState *lockstate) dbg("Calling __libc_atexit(rtld_exit (" PTR_FMT "))", (void*)exit_ptr); reg(exit_ptr); rtld_exit_ptr = make_rtld_function_pointer(rtld_nop_exit); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N rtld_exit_ptr = tramp_intern(NULL, &(struct tramp_data) { .target = rtld_exit_ptr, .defobj = &obj_rtld, @@ -4534,7 +4534,7 @@ dlsym(void *handle, const char *name) { void *retaddr; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N retaddr = c18n_return_address(); #else retaddr = __builtin_return_address(0); @@ -4552,7 +4552,7 @@ dlfunc(void *handle, const char *name) } rv; void *retaddr; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N retaddr = c18n_return_address(); #else retaddr = __builtin_return_address(0); @@ -4573,7 +4573,7 @@ dlvsym(void *handle, const char *name, const char *version) ventry.hash = elf_hash(version); ventry.flags= 0; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N retaddr = c18n_return_address(); #else retaddr = __builtin_return_address(0); @@ -4685,7 +4685,7 @@ dlinfo(void *handle, int request, void *p) if (handle == NULL || handle == RTLD_SELF) { void *retaddr; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N retaddr = c18n_return_address(); #else retaddr = __builtin_return_address(0); /* __GNUC__ only */ @@ -4759,7 +4759,7 @@ dl_iterate_phdr(__dl_iterate_hdr_callback callback, void *param) init_marker(&marker); error = 0; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N callback = tramp_intern(NULL, &(struct tramp_data) { .target = callback, .defobj = obj_from_addr(callback), @@ -5070,7 +5070,7 @@ get_program_var_addr(const char *name, RtldLockState *lockstate) return (NULL); if (ELF_ST_TYPE(req.sym_out->st_info) == STT_FUNC) { void *target = make_function_pointer(req.sym_out, req.defobj_out); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N target = tramp_intern(NULL, &(struct tramp_data) { .target = target, .defobj = req.defobj_out, @@ -5080,7 +5080,7 @@ get_program_var_addr(const char *name, RtldLockState *lockstate) return ((const void **)target); } else if (ELF_ST_TYPE(req.sym_out->st_info) == STT_GNU_IFUNC) { void *target = rtld_resolve_ifunc(req.defobj_out, req.sym_out); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N target = tramp_intern(NULL, &(struct tramp_data) { .target = target, .defobj = req.defobj_out, @@ -5817,14 +5817,14 @@ tls_get_addr_common(uintptr_t **dtvp, int index, size_t offset) dtv[index + 1] != 0)) return ((void *)(dtv[index + 1] + offset)); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N struct trusted_frame *tf; if (C18N_ENABLED) tf = push_dummy_rtld_trusted_frame(get_trusted_stk()); #endif ret = tls_get_addr_slow(dtvp, index, offset, false); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N if (C18N_ENABLED) tf = pop_dummy_rtld_trusted_frame(tf); #endif @@ -6188,7 +6188,7 @@ _rtld_allocate_tls(void *oldtls, size_t tcbsize, size_t tcbalign) wlock_acquire(rtld_bind_lock, &lockstate); ret = allocate_tls(globallist_curr(TAILQ_FIRST(&obj_list)), oldtls, tcbsize, tcbalign); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N /* * Create a fake wrapper TCB containing pointers to the real TCB and stack * lookup table. This is passed to the new thread as its initial TCB. Once @@ -6208,7 +6208,7 @@ _rtld_free_tls(void *tcb, size_t tcbsize, size_t tcbalign) RtldLockState lockstate; wlock_acquire(rtld_bind_lock, &lockstate); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N if (C18N_ENABLED) c18n_free_tcb(); #endif @@ -6228,7 +6228,7 @@ object_add_name(Obj_Entry *obj, const char *name) if (entry != NULL) { strcpy(entry->name, name); STAILQ_INSERT_TAIL(&obj->names, entry, link); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N if (C18N_ENABLED && obj->compart_id == 0) obj->compart_id = compart_id_allocate(entry->name); #endif diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h index 06e29c5a93d2..32790aa00ec2 100644 --- a/libexec/rtld-elf/rtld.h +++ b/libexec/rtld-elf/rtld.h @@ -277,7 +277,7 @@ typedef struct Struct_Obj_Entry { Ver_Entry *vertab; /* Versions required /defined by this object */ int vernum; /* Number of entries in vertab */ -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N uint16_t compart_id; const struct func_sig *sigtab; #endif diff --git a/libexec/rtld-elf/rtld_lock.c b/libexec/rtld-elf/rtld_lock.c index 8d9306dd93dd..9647c3977483 100644 --- a/libexec/rtld-elf/rtld_lock.c +++ b/libexec/rtld-elf/rtld_lock.c @@ -66,7 +66,7 @@ #include "rtld.h" #include "rtld_machdep.h" #include "rtld_libc.h" -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N #include "rtld_c18n.h" #endif @@ -248,7 +248,7 @@ thread_mask_clear(int mask) lockinfo.thread_clr_flag(mask); } -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N #define RTLD_LOCK_CNT 4 #else #define RTLD_LOCK_CNT 3 @@ -261,7 +261,7 @@ static struct rtld_lock { rtld_lock_t rtld_bind_lock = &rtld_locks[0]; rtld_lock_t rtld_libc_lock = &rtld_locks[1]; rtld_lock_t rtld_phdr_lock = &rtld_locks[2]; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N rtld_lock_t rtld_tramp_lock = &rtld_locks[3]; #endif @@ -410,7 +410,7 @@ _rtld_thread_init(struct RtldLockInfo *pli) SymLook req; void *locks[RTLD_LOCK_CNT]; int flags, i, res; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N struct RtldLockInfo tmplockinfo; #endif @@ -425,7 +425,7 @@ _rtld_thread_init(struct RtldLockInfo *pli) if (res == 0) lockinfo.rtli_version = pli->rtli_version; } -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N tmplockinfo = *pli; #define WRAP(_target, _valid, _reg_args, _mem_args, _ret_args) \ _target = tramp_intern(NULL, &(struct tramp_data) { \ diff --git a/libexec/rtld-elf/rtld_lock.h b/libexec/rtld-elf/rtld_lock.h index 5d13a6e4d664..c677029a67fa 100644 --- a/libexec/rtld-elf/rtld_lock.h +++ b/libexec/rtld-elf/rtld_lock.h @@ -82,7 +82,7 @@ typedef struct rtld_lock *rtld_lock_t; extern rtld_lock_t rtld_bind_lock; extern rtld_lock_t rtld_libc_lock; extern rtld_lock_t rtld_phdr_lock; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N extern rtld_lock_t rtld_tramp_lock; #endif diff --git a/sys/sys/link_elf.h b/sys/sys/link_elf.h index 88bd92a912c1..f999a296d945 100644 --- a/sys/sys/link_elf.h +++ b/sys/sys/link_elf.h @@ -74,7 +74,7 @@ struct r_debug { RT_DELETE /* removing a shared library */ } r_state; void *r_ldbase; /* Base address of rtld */ -#if defined(IN_RTLD) && defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#if defined(IN_RTLD) && defined(CHERI_LIB_C18N) enum { RCT_CONSISTENT, /* vector is stable */ RCT_ADD, /* adding a compartment */ From efcd3130c2acf96d9b807b5d4e7cfd8e70ac1690 Mon Sep 17 00:00:00 2001 From: Dapeng Gao Date: Tue, 18 Jun 2024 16:33:34 +0100 Subject: [PATCH 02/12] c18n: New {get,set}context APIs for libunwind _rtld_unw_getcontext_epilogue is removed because when c18n is not enabled, the trusted stack field of the unwind cursor is unused anyway, so there's no need to fill it. _rtld_unw_setcontext is no longer in assembly because it is now expected to be called before all registers are restored, so it does not need to restore any register. setjmp and longjmp are updated to use the slightly changed API signatures. --- lib/libc/aarch64/gen/_setjmp.S | 7 ++- lib/libc/aarch64/gen/setjmp.S | 7 ++- lib/libgcc_s/Symbol-c18n.map | 2 - libexec/rtld-elf/Symbol-c18n.map | 4 -- libexec/rtld-elf/aarch64/rtld_c18n_asm.S | 40 --------------- libexec/rtld-elf/rtld.c | 2 +- libexec/rtld-elf/rtld_c18n.c | 65 ++++++------------------ libexec/rtld-elf/rtld_c18n.h | 2 +- 8 files changed, 23 insertions(+), 106 deletions(-) diff --git a/lib/libc/aarch64/gen/_setjmp.S b/lib/libc/aarch64/gen/_setjmp.S index 66a239b563a3..b0f9c97236d9 100644 --- a/lib/libc/aarch64/gen/_setjmp.S +++ b/lib/libc/aarch64/gen/_setjmp.S @@ -81,9 +81,7 @@ ENTRY(_longjmp) /* Restore the stack pointer */ ldr REG(8), [REG(0)], #(REG_WIDTH) -#ifdef CHERI_LIB_C18N - mov c2, c8 -#else +#ifndef CHERI_LIB_C18N mov REGN(sp), REG(8) #endif @@ -105,11 +103,12 @@ ENTRY(_longjmp) /* Load the return value */ #ifdef CHERI_LIB_C18N - mov c3, c0 + ldr c2, [c0] #endif cmp x1, #0 csinc x0, x1, xzr, ne #ifdef CHERI_LIB_C18N + mov c1, c8 /* * Tail-call to restore Executive mode state */ diff --git a/lib/libc/aarch64/gen/setjmp.S b/lib/libc/aarch64/gen/setjmp.S index 164199ca695a..aefc6bffc094 100644 --- a/lib/libc/aarch64/gen/setjmp.S +++ b/lib/libc/aarch64/gen/setjmp.S @@ -110,9 +110,7 @@ ENTRY(longjmp) /* Restore the stack pointer */ ldr REG(8), [REG(0)], #(REG_WIDTH) -#ifdef CHERI_LIB_C18N - mov c2, c8 -#else +#ifndef CHERI_LIB_C18N mov REGN(sp), REG(8) #endif @@ -132,11 +130,12 @@ ENTRY(longjmp) /* Load the return value */ #ifdef CHERI_LIB_C18N - mov c3, c0 + ldr c2, [c0] #endif cmp x1, #0 csinc x0, x1, xzr, ne #ifdef CHERI_LIB_C18N + mov c1, c8 /* * Tail-call to restore Executive mode state */ diff --git a/lib/libgcc_s/Symbol-c18n.map b/lib/libgcc_s/Symbol-c18n.map index 7b817e2f56ef..6cddda834561 100644 --- a/lib/libgcc_s/Symbol-c18n.map +++ b/lib/libgcc_s/Symbol-c18n.map @@ -1,7 +1,5 @@ FBSDprivate_1.0 { _rtld_unw_getcontext; _rtld_unw_setcontext; - _rtld_unw_getcontext_unsealed; - _rtld_unw_setcontext_unsealed; _rtld_unw_getsealer; }; diff --git a/libexec/rtld-elf/Symbol-c18n.map b/libexec/rtld-elf/Symbol-c18n.map index 3729640162b9..2081b927e800 100644 --- a/libexec/rtld-elf/Symbol-c18n.map +++ b/libexec/rtld-elf/Symbol-c18n.map @@ -11,10 +11,6 @@ FBSDprivate_1.0 { _rtld_setjmp; _rtld_longjmp; _rtld_unw_getcontext; - _rtld_unw_getcontext_unsealed; _rtld_unw_setcontext; - _rtld_unw_setcontext_unsealed; _rtld_unw_getsealer; - _rtld_safebox_code; - _rtld_sandbox_code; }; diff --git a/libexec/rtld-elf/aarch64/rtld_c18n_asm.S b/libexec/rtld-elf/aarch64/rtld_c18n_asm.S index 17b699f5c533..f0b809f24754 100644 --- a/libexec/rtld-elf/aarch64/rtld_c18n_asm.S +++ b/libexec/rtld-elf/aarch64/rtld_c18n_asm.S @@ -34,46 +34,6 @@ * See rtld_c18n.h for an overview of the design. */ -/* - * The _rtld_unw_{get,set}context_epilogue functions are stack unwinding - * helpers. See the 'Stack unwinding' section in rtld_c18n.c. - */ -ENTRY(_rtld_unw_getcontext_epilogue) - /* - * FIXME: llvm-libunwind specific ABI. This should be better specified. - */ - mov c2, csp - str c2, [c1] - RETURN -END(_rtld_unw_getcontext_epilogue) - -/* - * XXX: If compartmentalisation is not enabled, _rtld_unw_setcontext_ptr is NULL - * and we simply restore a few registers and return via retr (back to Restricted - * mode). Otherwise, call _rtld_unw_setcontext_impl via a trampoline. - */ -ENTRY(_rtld_unw_setcontext) - ldr c16, _rtld_unw_setcontext_ptr - cbnz w16, 1f - /* - * FIXME: llvm-libunwind specific ABI. This should be better specified. - */ - mov c16, c2 - ldp c2, c3, [c3, #(-0x210 + 0x20)] - mov csp, c16 -#ifdef __ARM_MORELLO_PURECAP_BENCHMARK_ABI - RETURN -#else - retr c30 -#endif -1: -#ifdef __ARM_MORELLO_PURECAP_BENCHMARK_ABI - br x16 -#else - br c16 -#endif -END(_rtld_unw_setcontext) - /* * The _rtld_sighandler function is the actual signal handler passed to the * kernel when the user calls sigaction. It dispatches the signal to the diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c index 37d790bb6b74..bf7c191f6482 100644 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -1066,7 +1066,7 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) #ifdef CHERI_LIB_C18N if (C18N_ENABLED) - c18n_init2(&obj_rtld); + c18n_init2(); #endif /* diff --git a/libexec/rtld-elf/rtld_c18n.c b/libexec/rtld-elf/rtld_c18n.c index 99395627a166..3c6a3c8cb3a0 100644 --- a/libexec/rtld-elf/rtld_c18n.c +++ b/libexec/rtld-elf/rtld_c18n.c @@ -860,13 +860,6 @@ resolve_untrusted_stk_impl(stk_table_index index) /* * Stack unwinding */ -/* - * Assembly functions that are tail-called when compartmentalisation is - * disabled. - */ -uintptr_t _rtld_unw_getcontext_epilogue(uintptr_t, void **); -struct jmp_args _rtld_unw_setcontext_epilogue(struct jmp_args, void *, void **); - static void * unwind_cursor(void) { @@ -899,22 +892,13 @@ _rtld_setjmp(uintptr_t ret, void **buf) uintptr_t _rtld_unw_getcontext(uintptr_t ret, void **buf) { - if (!C18N_ENABLED) { - __attribute__((musttail)) - return (_rtld_unw_getcontext_epilogue(ret, buf)); - } - *buf = cheri_seal(unwind_cursor(), sealer_unwbuf); + if (C18N_ENABLED) + *buf = cheri_seal(unwind_cursor(), sealer_unwbuf); return (ret); } -/* - * Returning this struct allows us to control the content of unused return value - * registers. - */ -struct jmp_args { uintptr_t ret1; uintptr_t ret2; }; - -static struct jmp_args -unwind_stack(struct jmp_args ret, void *rcsp, struct trusted_frame *target) +static uintptr_t +unwind_stack(uintptr_t ret, void *rcsp, struct trusted_frame *target) { /* * This helper is used by functions like longjmp. Before longjmp is @@ -1000,19 +984,21 @@ unwind_stack(struct jmp_args ret, void *rcsp, struct trusted_frame *target) return (ret); } -struct jmp_args _rtld_longjmp(struct jmp_args, void *, void **); -struct jmp_args _rtld_unw_setcontext_impl(struct jmp_args, void *, void **); +uintptr_t _rtld_longjmp(uintptr_t, void *, void *); +uintptr_t _rtld_unw_setcontext(uintptr_t, void *, void *); -struct jmp_args -_rtld_longjmp(struct jmp_args ret, void *rcsp, void **buf) +uintptr_t +_rtld_longjmp(uintptr_t ret, void *rcsp, void *csp) { - return (unwind_stack(ret, rcsp, cheri_unseal(*buf, sealer_jmpbuf))); + return (unwind_stack(ret, rcsp, cheri_unseal(csp, sealer_jmpbuf))); } -struct jmp_args -_rtld_unw_setcontext_impl(struct jmp_args ret, void *rcsp, void **buf) +uintptr_t +_rtld_unw_setcontext(uintptr_t ret, void *rcsp, void *csp) { - return (unwind_stack(ret, rcsp, cheri_unseal(*buf, sealer_unwbuf))); + if (C18N_ENABLED) + ret = unwind_stack(ret, rcsp, cheri_unseal(csp, sealer_unwbuf)); + return (ret); } uintptr_t _rtld_unw_getsealer(void); @@ -1612,16 +1598,8 @@ c18n_init(Obj_Entry *obj_rtld, Elf_Auxinfo *aux_info[]) */ #define MAX_TRAMP_PG_SIZE (4 * 1024 * 1024) -/* - * XXX: Manually wrap _rtld_unw_setcontext_impl in a trampoline for now because - * it is called via a function pointer. - */ -extern struct jmp_args (*_rtld_unw_setcontext_ptr)(struct jmp_args, void *, - void **); -struct jmp_args (*_rtld_unw_setcontext_ptr)(struct jmp_args, void *, void **); - void -c18n_init2(Obj_Entry *obj_rtld) +c18n_init2(void) { uintptr_t sealer; @@ -1670,19 +1648,6 @@ c18n_init2(Obj_Entry *obj_rtld) assert(tramp_pg_size > 0); atomic_store_explicit(&tramp_pgs.head, tramp_pg_new(NULL), memory_order_relaxed); - - /* - * XXX: Manually wrap _rtld_unw_setcontext_impl in a trampoline for now - * because it is called via a function pointer. - */ - _rtld_unw_setcontext_ptr = tramp_intern(NULL, &(struct tramp_data) { - .target = &_rtld_unw_setcontext_impl, - .defobj = obj_rtld, - .sig = (struct func_sig) { - .valid = true, - .reg_args = 4, .mem_args = false, .ret_args = TWO - } - }); } /* diff --git a/libexec/rtld-elf/rtld_c18n.h b/libexec/rtld-elf/rtld_c18n.h index 646d48b23779..9ebe627f98f5 100644 --- a/libexec/rtld-elf/rtld_c18n.h +++ b/libexec/rtld-elf/rtld_c18n.h @@ -229,5 +229,5 @@ void *_rtld_tlsdesc_undef_c18n(void *); void *_rtld_tlsdesc_dynamic_c18n(void *); void c18n_init(Obj_Entry *, Elf_Auxinfo *[]); -void c18n_init2(Obj_Entry *); +void c18n_init2(void); #endif From 58d920d77a5e55163de19323054e4d500971cd99 Mon Sep 17 00:00:00 2001 From: Dapeng Gao Date: Fri, 21 Jun 2024 16:10:41 +0100 Subject: [PATCH 03/12] c18n: Expose new unwinding APIs Previously, libunwind hard-codes knowledge about the layout of the trusted frame and has read access to the trusted stack. This is fragile and insecure. Now, the task of extracting the relevant registers from the trusted stack is delegated to RTLD, and libunwind no longer has access to the trusted stack. In addition, unify the APIs exposed to setjmp/longjmp and libunwind. --- lib/libc/aarch64/gen/_setjmp.S | 4 +- lib/libc/aarch64/gen/setjmp.S | 9 +- lib/libgcc_s/Symbol-c18n.map | 8 +- libexec/rtld-elf/Symbol-c18n.map | 10 +-- libexec/rtld-elf/aarch64/rtld_c18n_machdep.h | 34 +------ libexec/rtld-elf/rtld_c18n.c | 93 +++++++++----------- libexec/rtld-elf/rtld_c18n.h | 46 +++++++++- libexec/rtld-elf/rtld_c18n_policy.txt | 14 +-- 8 files changed, 111 insertions(+), 107 deletions(-) diff --git a/lib/libc/aarch64/gen/_setjmp.S b/lib/libc/aarch64/gen/_setjmp.S index b0f9c97236d9..ce8a7fe0ad3b 100644 --- a/lib/libc/aarch64/gen/_setjmp.S +++ b/lib/libc/aarch64/gen/_setjmp.S @@ -63,7 +63,7 @@ ENTRY(_setjmp) /* * Tail-call to save Executive mode state */ - b _rtld_setjmp + b c18n_get_trusted_stk #else RETURN #endif @@ -112,7 +112,7 @@ ENTRY(_longjmp) /* * Tail-call to restore Executive mode state */ - b _rtld_longjmp + b c18n_unwind_trusted_stk #else RETURN #endif diff --git a/lib/libc/aarch64/gen/setjmp.S b/lib/libc/aarch64/gen/setjmp.S index aefc6bffc094..47fc275c527a 100644 --- a/lib/libc/aarch64/gen/setjmp.S +++ b/lib/libc/aarch64/gen/setjmp.S @@ -32,11 +32,6 @@ #include #include -#ifdef CHERI_LIB_C18N -.weak _rtld_setjmp -.weak _rtld_longjmp -#endif - ENTRY(setjmp) sub REGN(sp), REGN(sp), #(REG_WIDTH * 2) stp REG(0), REGN(lr), [REGN(sp)] @@ -78,7 +73,7 @@ ENTRY(setjmp) /* * Tail-call to save Executive mode state */ - b _rtld_setjmp + b c18n_get_trusted_stk #else RETURN #endif @@ -139,7 +134,7 @@ ENTRY(longjmp) /* * Tail-call to restore Executive mode state */ - b _rtld_longjmp + b c18n_unwind_trusted_stk #else RETURN #endif diff --git a/lib/libgcc_s/Symbol-c18n.map b/lib/libgcc_s/Symbol-c18n.map index 6cddda834561..d9aa2213192d 100644 --- a/lib/libgcc_s/Symbol-c18n.map +++ b/lib/libgcc_s/Symbol-c18n.map @@ -1,5 +1,7 @@ FBSDprivate_1.0 { - _rtld_unw_getcontext; - _rtld_unw_setcontext; - _rtld_unw_getsealer; + c18n_get_trusted_stk; + c18n_unwind_trusted_stk; + c18n_is_enabled; + c18n_is_tramp; + c18n_pop_trusted_stk; }; diff --git a/libexec/rtld-elf/Symbol-c18n.map b/libexec/rtld-elf/Symbol-c18n.map index 2081b927e800..2e7fe32e69b7 100644 --- a/libexec/rtld-elf/Symbol-c18n.map +++ b/libexec/rtld-elf/Symbol-c18n.map @@ -8,9 +8,9 @@ FBSDprivate_1.0 { _rtld_sighandler; _rtld_siginvoke; _rtld_sigaction; - _rtld_setjmp; - _rtld_longjmp; - _rtld_unw_getcontext; - _rtld_unw_setcontext; - _rtld_unw_getsealer; + c18n_get_trusted_stk; + c18n_unwind_trusted_stk; + c18n_is_enabled; + c18n_is_tramp; + c18n_pop_trusted_stk; }; diff --git a/libexec/rtld-elf/aarch64/rtld_c18n_machdep.h b/libexec/rtld-elf/aarch64/rtld_c18n_machdep.h index 5e2607f42f08..bd1330ea55c9 100644 --- a/libexec/rtld-elf/aarch64/rtld_c18n_machdep.h +++ b/libexec/rtld-elf/aarch64/rtld_c18n_machdep.h @@ -133,7 +133,7 @@ set_untrusted_stk(const void *sp) } #endif -struct trusted_frame { +struct compart_state { void *fp; void *pc; /* @@ -145,38 +145,6 @@ struct trusted_frame { * caller made the call. */ void *sp; - /* - * INVARIANT: This field contains the top of the caller's stack when the - * caller was last entered. - */ - void *osp; - /* - * Address of the previous trusted frame - */ - struct trusted_frame *previous; - /* - * Compartment ID of the caller - */ - stk_table_index caller; - /* - * Zeros - */ - uint16_t zeros; - /* - * Compartment ID of the callee - */ - stk_table_index callee; - /* - * Number of return value registers, encoded in enum tramp_ret_args - */ - uint8_t ret_args : 2; - uint16_t reserved : 14; - /* - * This field contains the code address in the trampoline that the - * callee should return to. This is used by trampolines to detect cross- - * compartment tail-calls. - */ - ptraddr_t landing; }; #endif #endif diff --git a/libexec/rtld-elf/rtld_c18n.c b/libexec/rtld-elf/rtld_c18n.c index 3c6a3c8cb3a0..82f1e48018ff 100644 --- a/libexec/rtld-elf/rtld_c18n.c +++ b/libexec/rtld-elf/rtld_c18n.c @@ -87,7 +87,7 @@ _Static_assert( TRUSTED_FRAME_SIZE * sizeof(uintptr_t) == sizeof(struct trusted_frame), "Unexpected struct trusted_frame size"); _Static_assert( - TRUSTED_FRAME_SP_OSP == offsetof(struct trusted_frame, sp), + TRUSTED_FRAME_SP_OSP == offsetof(struct trusted_frame, state.sp), "Unexpected struct trusted_frame member offset"); _Static_assert( TRUSTED_FRAME_PREV == offsetof(struct trusted_frame, previous), @@ -117,8 +117,7 @@ _Static_assert( * Sealers for RTLD privileged information */ static uintptr_t sealer_tcb; -static uintptr_t sealer_jmpbuf; -static uintptr_t sealer_unwbuf; +static uintptr_t sealer_trusted_stk; uintptr_t sealer_pltgot, sealer_tramp; @@ -859,9 +858,19 @@ resolve_untrusted_stk_impl(stk_table_index index) /* * Stack unwinding + * + * APIs exposed to stack unwinders (e.g., libc setjmp/longjmp and libunwind) */ -static void * -unwind_cursor(void) +uintptr_t c18n_get_trusted_stk(uintptr_t, struct trusted_frame **); +uintptr_t c18n_unwind_trusted_stk(uintptr_t, void *, struct trusted_frame *); + +bool c18n_is_enabled(void); +bool c18n_is_tramp(ptraddr_t, struct trusted_frame *); +struct trusted_frame *c18n_pop_trusted_stk(struct compart_state *, + struct trusted_frame *); + +uintptr_t +c18n_get_trusted_stk(uintptr_t ret, struct trusted_frame **buf) { /* * This helper is used by functions like setjmp. Before setjmp is @@ -875,30 +884,14 @@ unwind_cursor(void) * buffer. */ - return (get_trusted_stk()->previous); -} - -uintptr_t _rtld_setjmp(uintptr_t, void **); -uintptr_t _rtld_unw_getcontext(uintptr_t, void **); -uintptr_t _rtld_unw_getcontext_unsealed(uintptr_t, void **); - -uintptr_t -_rtld_setjmp(uintptr_t ret, void **buf) -{ - *buf = cheri_seal(unwind_cursor(), sealer_jmpbuf); - return (ret); -} - -uintptr_t -_rtld_unw_getcontext(uintptr_t ret, void **buf) -{ if (C18N_ENABLED) - *buf = cheri_seal(unwind_cursor(), sealer_unwbuf); + *buf = cheri_seal(get_trusted_stk()->previous, + sealer_trusted_stk); return (ret); } -static uintptr_t -unwind_stack(uintptr_t ret, void *rcsp, struct trusted_frame *target) +uintptr_t +c18n_unwind_trusted_stk(uintptr_t ret, void *rcsp, struct trusted_frame *target) { /* * This helper is used by functions like longjmp. Before longjmp is @@ -920,6 +913,9 @@ unwind_stack(uintptr_t ret, void *rcsp, struct trusted_frame *target) struct trusted_frame *cur, *tf; sigset_t nset, oset; + if (!C18N_ENABLED) + return (ret); + /* * Make the function re-entrant by blocking all signals. */ @@ -927,6 +923,7 @@ unwind_stack(uintptr_t ret, void *rcsp, struct trusted_frame *target) sigprocmask(SIG_SETMASK, &nset, &oset); tf = get_trusted_stk(); + target = cheri_unseal(target, sealer_trusted_stk); if (!cheri_is_subset(tf, target) || tf->previous >= target) { rtld_fdprintf(STDERR_FILENO, @@ -974,7 +971,7 @@ unwind_stack(uintptr_t ret, void *rcsp, struct trusted_frame *target) abort(); } - tf->sp = rcsp; + tf->state.sp = rcsp; tf->osp = *ospp; tf->previous = cur; tf->caller = index; @@ -984,28 +981,25 @@ unwind_stack(uintptr_t ret, void *rcsp, struct trusted_frame *target) return (ret); } -uintptr_t _rtld_longjmp(uintptr_t, void *, void *); -uintptr_t _rtld_unw_setcontext(uintptr_t, void *, void *); - -uintptr_t -_rtld_longjmp(uintptr_t ret, void *rcsp, void *csp) +bool +c18n_is_enabled(void) { - return (unwind_stack(ret, rcsp, cheri_unseal(csp, sealer_jmpbuf))); + return (C18N_ENABLED); } -uintptr_t -_rtld_unw_setcontext(uintptr_t ret, void *rcsp, void *csp) +bool +c18n_is_tramp(ptraddr_t pc, struct trusted_frame *tf) { - if (C18N_ENABLED) - ret = unwind_stack(ret, rcsp, cheri_unseal(csp, sealer_unwbuf)); - return (ret); + tf = cheri_unseal(tf, sealer_trusted_stk); + return (pc == tf->landing); } -uintptr_t _rtld_unw_getsealer(void); -uintptr_t -_rtld_unw_getsealer(void) +struct trusted_frame * +c18n_pop_trusted_stk(struct compart_state *state, struct trusted_frame *tf) { - return (sealer_unwbuf); + tf = cheri_unseal(tf, sealer_trusted_stk); + *state = tf->state; + return (cheri_seal(tf->previous, sealer_trusted_stk)); } /* @@ -1195,9 +1189,9 @@ tramp_hook_impl(int event, const struct tramp_header *hdr, memcpy(ut.sig, C18N_UTRACE_SIG, C18N_UTRACE_SIG_SZ); ut.event = event; ut.symnum = hdr->symnum; - ut.fp = tf->fp; - ut.pc = tf->pc; - ut.sp = tf->sp; + ut.fp = tf->state.fp; + ut.pc = tf->state.pc; + ut.sp = tf->state.sp; ut.osp = tf->osp; ut.previous = tf->previous; memcpy(&ut.fsig, &hdr->sig, sizeof(ut.fsig)); @@ -1616,10 +1610,7 @@ c18n_init2(void) sealer_tcb = cheri_setboundsexact(sealer, 1); sealer += 1; - sealer_jmpbuf = cheri_setboundsexact(sealer, 1); - sealer += 1; - - sealer_unwbuf = cheri_setboundsexact(sealer, 1); + sealer_trusted_stk = cheri_setboundsexact(sealer, 1); sealer += 1; sealer_tramp = cheri_setboundsexact(sealer, C18N_FUNC_SIG_COUNT); @@ -1942,7 +1933,9 @@ _rtld_sighandler_impl(int sig, siginfo_t *info, ucontext_t *ucp, void *nsp) */ ntf = tf - 2; *ntf = (struct trusted_frame) { - .sp = nsp, + .state = (struct compart_state) { + .sp = nsp + }, .osp = osp, .previous = tf, .caller = intr_idx, @@ -1997,7 +1990,7 @@ _rtld_sighandler_impl(int sig, siginfo_t *info, ucontext_t *ucp, void *nsp) * compartment. */ #ifndef __ARM_MORELLO_PURECAP_BENCHMARK_ABI - set_untrusted_stk(ntf->sp); + set_untrusted_stk(ntf->state.sp); #endif } diff --git a/libexec/rtld-elf/rtld_c18n.h b/libexec/rtld-elf/rtld_c18n.h index 9ebe627f98f5..e3aefcd3cdde 100644 --- a/libexec/rtld-elf/rtld_c18n.h +++ b/libexec/rtld-elf/rtld_c18n.h @@ -120,6 +120,48 @@ struct stk_table { #include "rtld_c18n_machdep.h" +struct trusted_frame { + /* + * Architecture-specific callee-saved registers, including fp, sp, and + * the return address + */ + struct compart_state state; + /* + * INVARIANT: This field contains the top of the caller's stack when the + * caller was last entered. + */ + void *osp; + /* + * Address of the previous trusted frame + */ + struct trusted_frame *previous; + /* + * Compartment ID of the caller + */ + stk_table_index caller; + /* + * This padding space must be filled with zeros so that an optimised + * trampoline can use a wide load to load multiple fields of the trusted + * frame and then use a word-sized register to extract the caller field. + */ + uint16_t zeros; + /* + * Compartment ID of the callee + */ + stk_table_index callee; + /* + * Number of return value registers, encoded in enum tramp_ret_args + */ + uint8_t ret_args : 2; + uint16_t reserved : 14; + /* + * This field contains the code address in the trampoline that the + * callee should return to. This is used by trampolines to detect cross- + * compartment tail-calls. + */ + ptraddr_t landing; +}; + struct tcb *c18n_allocate_tcb(struct tcb *); void c18n_free_tcb(void); @@ -217,8 +259,8 @@ func_sig_legal(struct func_sig sig) /* * This macro can only be used in a function directly invoked by a trampoline. */ -#define c18n_return_address() \ - (C18N_ENABLED ? get_trusted_stk()->pc : __builtin_return_address(0)) +#define c18n_return_address() (C18N_ENABLED ? \ + get_trusted_stk()->state.pc : __builtin_return_address(0)) void *_rtld_sandbox_code(void *, struct func_sig); void *_rtld_safebox_code(void *, struct func_sig); diff --git a/libexec/rtld-elf/rtld_c18n_policy.txt b/libexec/rtld-elf/rtld_c18n_policy.txt index d373837982ab..a3840600ddd3 100644 --- a/libexec/rtld-elf/rtld_c18n_policy.txt +++ b/libexec/rtld-elf/rtld_c18n_policy.txt @@ -56,11 +56,15 @@ export to [TCB] _rtld_sighandler _rtld_siginvoke _rtld_sigaction - _rtld_setjmp - _rtld_longjmp + +callee [RTLD] +export to [TCB] +export to [libunwind] + c18n_get_trusted_stk + c18n_unwind_trusted_stk callee [RTLD] export to [libunwind] - _rtld_unw_getcontext - _rtld_unw_setcontext - _rtld_unw_getsealer + c18n_is_enabled + c18n_is_tramp + c18n_pop_trusted_stk From 4073c144dd302ca4d8e32e2431cfe758c9ad7c99 Mon Sep 17 00:00:00 2001 From: Dapeng Gao Date: Tue, 18 Jun 2024 16:41:10 +0100 Subject: [PATCH 04/12] libunwind: Simplify _unw_{get,set}context under c18n Assembly stubs for _rtld_unw_{get,set}context are removed. Instead, turn calls to these functions into no-ops when they are not defined by RTLD. --- .../src/UnwindRegistersRestore.S | 40 +++++-------------- .../src/UnwindRegistersSave.S | 18 +++++---- 2 files changed, 22 insertions(+), 36 deletions(-) diff --git a/contrib/subrepo-cheri-libunwind/src/UnwindRegistersRestore.S b/contrib/subrepo-cheri-libunwind/src/UnwindRegistersRestore.S index e20d2b125c95..351ef4e3bdbb 100644 --- a/contrib/subrepo-cheri-libunwind/src/UnwindRegistersRestore.S +++ b/contrib/subrepo-cheri-libunwind/src/UnwindRegistersRestore.S @@ -703,25 +703,6 @@ Lnovec: #elif defined(__aarch64__) -// -// extern "C" void __rtld_unw_setcontext(void *c0, void *c1, -// void *rcsp, void **sealed_ecsp); -// -#if defined(__CHERI_PURE_CAPABILITY__) -DEFINE_LIBUNWIND_FUNCTION(__rtld_unw_setcontext) - mov c16, c2 - ldp c2, c3, [c3, #(-0x210 + 0x20)] - mov csp, c16 -#ifdef __ARM_MORELLO_PURECAP_BENCHMARK_ABI - and x30, x30, #~1 - ret x30 -#else - ret -#endif -END_LIBUNWIND_FUNCTION(__rtld_unw_setcontext) -WEAK_ALIAS(__rtld_unw_setcontext, _rtld_unw_setcontext) -#endif - // // extern "C" void __libunwind_Registers_arm64_jumpto(Registers_arm64 *); // @@ -731,8 +712,12 @@ WEAK_ALIAS(__rtld_unw_setcontext, _rtld_unw_setcontext) .p2align 2 DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_arm64_jumpto) #ifdef __CHERI_PURE_CAPABILITY__ + ldr c1, [c0, #0x1f0] // Pass the target untrusted stack pointer + ldr c2, [c0, #0x210] // Pass the target trusted stack pointer + bl _rtld_unw_setcontext + // skip restore of c0,c1 for now - // also skip restoring c2 and c3 because they will get clobbered later on + ldp c2, c3, [c0, #0x020] ldp c4, c5, [c0, #0x040] ldp c6, c7, [c0, #0x060] ldp c8, c9, [c0, #0x080] @@ -772,17 +757,14 @@ DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_arm64_jumpto) // context struct, because it is allocated on the stack, and an exception // could clobber the de-allocated portion of the stack after csp has been // restored. - ldr c2, [c0, #0x1f0] - add c3, c0, #0x210 - ldp c0, c1, [c0, #0x000] - // XXX: variant PCS is not yet supported by rtld, work around it - // using a function pointer. - adrp c16, :got:_rtld_unw_setcontext - ldr c16, [c16, :got_lo12:_rtld_unw_setcontext] + ldr c16, [c0, #0x1f0] + ldp c0, c1, [c0, #0x000] // restore c0,c1 + mov csp,c16 // restore csp #ifdef __ARM_MORELLO_PURECAP_BENCHMARK_ABI - br x16 + and x30, x30, #~1 + ret x30 // jump to pc #else - br c16 + ret // jump to pcc #endif #else // skip restore of x0,x1 for now diff --git a/contrib/subrepo-cheri-libunwind/src/UnwindRegistersSave.S b/contrib/subrepo-cheri-libunwind/src/UnwindRegistersSave.S index 8edcd4cb51d0..bb2dd7cb8bf3 100644 --- a/contrib/subrepo-cheri-libunwind/src/UnwindRegistersSave.S +++ b/contrib/subrepo-cheri-libunwind/src/UnwindRegistersSave.S @@ -837,13 +837,17 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) #elif defined(__aarch64__) -#if defined(__CHERI_PURE_CAPABILITY__) -DEFINE_LIBUNWIND_FUNCTION(__rtld_unw_getcontext) - mov c2, csp - str c2, [c1] - ret c30 -END_LIBUNWIND_FUNCTION(__rtld_unw_getcontext) -WEAK_ALIAS(__rtld_unw_getcontext, _rtld_unw_getcontext) +#ifdef __CHERI_PURE_CAPABILITY__ +DEFINE_LIBUNWIND_FUNCTION(__rtld_unw_noop) +#ifdef __ARM_MORELLO_PURECAP_BENCHMARK_ABI + and x30, x30, #~1 + ret x30 +#else + ret +#endif +END_LIBUNWIND_FUNCTION(__rtld_unw_noop) +WEAK_ALIAS(__rtld_unw_noop, _rtld_unw_getcontext) +WEAK_ALIAS(__rtld_unw_noop, _rtld_unw_setcontext) #endif // From 46644133e3758da79f9f55f4bd6be33e98197c97 Mon Sep 17 00:00:00 2001 From: Dapeng Gao Date: Fri, 21 Jun 2024 16:21:55 +0100 Subject: [PATCH 05/12] libunwind: Use APIs exposed by RTLD to unwind the trusted stack --- .../include/__libunwind_config.h | 5 - .../src/AddressSpace.hpp | 24 ----- .../src/CompartmentInfo.hpp | 78 +++++++++++--- .../src/DwarfInstructions.hpp | 101 ++---------------- .../src/UnwindRegistersRestore.S | 2 +- .../src/UnwindRegistersSave.S | 12 ++- .../subrepo-cheri-libunwind/src/libunwind.cpp | 6 -- 7 files changed, 83 insertions(+), 145 deletions(-) diff --git a/contrib/subrepo-cheri-libunwind/include/__libunwind_config.h b/contrib/subrepo-cheri-libunwind/include/__libunwind_config.h index 5b3f0bff628c..cc14c084072c 100644 --- a/contrib/subrepo-cheri-libunwind/include/__libunwind_config.h +++ b/contrib/subrepo-cheri-libunwind/include/__libunwind_config.h @@ -235,9 +235,4 @@ # define _LIBUNWIND_HIGHEST_DWARF_REGISTER 287 #endif // _LIBUNWIND_IS_NATIVE_ONLY -#if defined(_LIBUNWIND_CHERI_C18N_SUPPORT) && \ - !defined(_LIBUNWIND_TARGET_AARCH64) -# error "LIBUNWIND_CHERI_C18N_SUPPORT is only supported on Morello" -#endif - #endif // ____LIBUNWIND_CONFIG_H__ diff --git a/contrib/subrepo-cheri-libunwind/src/AddressSpace.hpp b/contrib/subrepo-cheri-libunwind/src/AddressSpace.hpp index 17b05a224ebf..892c97f71873 100644 --- a/contrib/subrepo-cheri-libunwind/src/AddressSpace.hpp +++ b/contrib/subrepo-cheri-libunwind/src/AddressSpace.hpp @@ -321,12 +321,6 @@ class _LIBUNWIND_HIDDEN LocalAddressSpace { return get(addr); } capability_t getCapability(pint_t addr) { return get(addr); } -#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_CHERI_C18N_SUPPORT) - static pint_t getUnwindSealer(); - static bool isValidSealer(pint_t sealer) { - return __builtin_cheri_tag_get(sealer); - } -#endif // __CHERI_PURE_CAPABILITY__ && _LIBUNWIND_CHERI_C18N_SUPPORT __attribute__((always_inline)) uintptr_t getP(pint_t addr); uint64_t getRegister(pint_t addr); @@ -415,24 +409,6 @@ inline uint64_t LocalAddressSpace::getRegister(pint_t addr) { #endif } -#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_CHERI_C18N_SUPPORT) -extern "C" { -/// Call into the RTLD to get a sealer capability. This sealer will be used to -/// seal information in the unwinding context. -uintptr_t _rtld_unw_getsealer(); -uintptr_t __rtld_unw_getsealer(); -_LIBUNWIND_HIDDEN uintptr_t __rtld_unw_getsealer() { - return (uintptr_t)0; -} -_LIBUNWIND_WEAK_ALIAS(__rtld_unw_getsealer, _rtld_unw_getsealer) -} - -/// C++ wrapper for calling into RTLD. -inline LocalAddressSpace::pint_t LocalAddressSpace::getUnwindSealer() { - return _rtld_unw_getsealer(); -} -#endif // __CHERI_PURE_CAPABILITY__ && _LIBUNWIND_CHERI_C18N_SUPPORT - /// Read a ULEB128 into a 64-bit word. inline uint64_t LocalAddressSpace::getULEB128(pint_t &addr, pint_t end) { const uint8_t *p = (uint8_t *)addr; diff --git a/contrib/subrepo-cheri-libunwind/src/CompartmentInfo.hpp b/contrib/subrepo-cheri-libunwind/src/CompartmentInfo.hpp index 08ff723c7220..e9429becdfa7 100644 --- a/contrib/subrepo-cheri-libunwind/src/CompartmentInfo.hpp +++ b/contrib/subrepo-cheri-libunwind/src/CompartmentInfo.hpp @@ -14,20 +14,72 @@ #define __COMPARTMENT_INFO_HPP__ namespace libunwind { -class _LIBUNWIND_HIDDEN CompartmentInfo { -public: -#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_CHERI_C18N_SUPPORT) - static CompartmentInfo sThisCompartmentInfo; - // Per-architecture trusted stack frame layout. + +extern "C" { + +struct trusted_frame; + +// Must mirror the layout in rtld_c18n_machdep.h #if defined(_LIBUNWIND_TARGET_AARCH64) - static const uint32_t kNewSPOffset = 12 * sizeof(void *); - static const uint32_t kNextOffset = 14 * sizeof(void *); - static const uint32_t kCalleeSavedOffset = 2 * sizeof(void *); - static const uint32_t kCalleeSavedCount = 10; - static const uint32_t kReturnAddressOffset = 15 * sizeof(void *) + 8; - static const uint32_t kPCOffset = sizeof(void *); -#endif // _LIBUNWIND_TARGET_AARCH64 -#endif // __CHERI_PURE_CAPABILITY__ && _LIBUNWIND_CHERI_C18N_SUPPORT +struct compart_state { + void *fp; + void *pc; + void *regs[10]; // c19 to c28 + void *sp; +}; +#else +# error "LIBUNWIND_CHERI_C18N_SUPPORT is not supported on this target" +#endif + +#pragma weak c18n_is_enabled +bool c18n_is_enabled(void) { + return false; +}; + +#pragma weak c18n_is_tramp +bool c18n_is_tramp(ptraddr_t, struct trusted_frame *); + +#pragma weak c18n_pop_trusted_stk +struct trusted_frame * +c18n_pop_trusted_stk(struct compart_state *, struct trusted_frame *); +} + +template +struct CompartmentInfo { + typedef typename A::pint_t pint_t; + + static bool isC18NEnabled() { + return c18n_is_enabled(); + } + + static bool isC18NTramp(pint_t pc, pint_t tf) { + return c18n_is_tramp(pc, (struct trusted_frame *)tf); + } + + static pint_t fillC18NState(R &newRegisters, pint_t tf) { + struct compart_state state; + tf = (pint_t)c18n_pop_trusted_stk(&state, (struct trusted_frame *)tf); + + newRegisters.setTrustedStack(tf); + CHERI_DBG("C18N: SET TRUSTED STACK %#p\n", (void *)tf); + + newRegisters.setFP((pint_t)state.fp); + CHERI_DBG("C18N: SET FP %#p\n", state.fp); + + newRegisters.setSP((pint_t)state.sp); + CHERI_DBG("C18N: SET SP: %#p\n", state.sp); + + for (size_t i = 0; i < sizeof(state.regs) / sizeof(*state.regs); ++i) { + newRegisters.setCapabilityRegister(UNW_ARM64_C19 + i, + (pint_t)state.regs[i]); + CHERI_DBG("C18N: SET REGISTER: %lu (%s): %#p\n", + UNW_ARM64_C19 + i, + newRegisters.getRegisterName(UNW_ARM64_C19 + i), + state.regs[i]); + } + + return (pint_t)state.pc; + } }; } // namespace libunwind #endif // __COMPARTMENT_INFO_HPP__ diff --git a/contrib/subrepo-cheri-libunwind/src/DwarfInstructions.hpp b/contrib/subrepo-cheri-libunwind/src/DwarfInstructions.hpp index dc55191e8c93..32a0dcc32aa1 100644 --- a/contrib/subrepo-cheri-libunwind/src/DwarfInstructions.hpp +++ b/contrib/subrepo-cheri-libunwind/src/DwarfInstructions.hpp @@ -20,7 +20,9 @@ #include "Registers.hpp" #include "DwarfParser.hpp" #include "config.h" +#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_CHERI_C18N_SUPPORT) #include "CompartmentInfo.hpp" +#endif // __CHERI_PURE_CAPABILITY__ && _LIBUNWIND_CHERI_C18N_SUPPORT namespace libunwind { @@ -55,14 +57,6 @@ class DwarfInstructions { typedef typename CFI_Parser::FDE_Info FDE_Info; typedef typename CFI_Parser::CIE_Info CIE_Info; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_CHERI_C18N_SUPPORT) - static pint_t restoreRegistersFromSandbox(pint_t csp, A &addressSpace, - R &newRegisters, - CompartmentInfo &CI, pint_t sealer); - static bool isCompartmentTransitionTrampoline(pint_t ecsp, A &addressSpace, - CompartmentInfo &CI, - pint_t returnAddress); -#endif static pint_t evaluateExpression(pint_t expression, A &addressSpace, const R ®isters, pint_t initialStackValue); @@ -105,6 +99,9 @@ class DwarfInstructions { static bool getRA_SIGN_STATE(A &addressSpace, R registers, pint_t cfa, PrologInfo &prolog); #endif +#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_CHERI_C18N_SUPPORT) + static constexpr CompartmentInfo CompartInfo{}; +#endif }; template @@ -255,75 +252,6 @@ bool DwarfInstructions::getRA_SIGN_STATE(A &addressSpace, R registers, } #endif -#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_CHERI_C18N_SUPPORT) -#if defined(_LIBUNWIND_TARGET_AARCH64) -template -typename A::pint_t DwarfInstructions::restoreRegistersFromSandbox( - pint_t csp, A &addressSpace, R &newRegisters, CompartmentInfo &CI, - pint_t sealer) { - // Get the unsealed executive CSP - assert(__builtin_cheri_tag_get((void *)csp) && - "Executive stack should be tagged!"); - // Derive the new executive CSP - pint_t nextCSP = addressSpace.getP(csp + CI.kNextOffset); - // Seal ECSP - nextCSP = __builtin_cheri_seal(nextCSP, sealer); - assert(__builtin_cheri_tag_get((void *)nextCSP) && - "Next executive stack should be tagged!"); - CHERI_DBG("SANDBOX: SETTING EXECUTIVE CSP %#p\n", (void *)nextCSP); - newRegisters.setTrustedStack(nextCSP); - // Restore the next RCSP - pint_t nextRCSP = addressSpace.getP(csp + CI.kNewSPOffset); - newRegisters.setSP(nextRCSP); - CHERI_DBG("SANDBOX: SETTING RESTRICTED CSP: %#p\n", - (void *)newRegisters.getSP()); - // Restore callee-saved registers - // Restore: c19-c28 - for (size_t i = 0, offset = CI.kCalleeSavedOffset; i < CI.kCalleeSavedCount; - ++i, offset += sizeof(void *)) { - pint_t regValue = addressSpace.getP(csp + offset); - newRegisters.setCapabilityRegister(UNW_ARM64_C19 + i, regValue); - CHERI_DBG("SETTING CALLEE SAVED CAPABILITY REGISTER: %lu (%s): %#p " - "(offset=%zu)\n", - UNW_ARM64_C19 + i, - newRegisters.getRegisterName(UNW_ARM64_C19 + i), (void *)regValue, - offset); - } - // Restore the frame pointer - pint_t newFP = addressSpace.getP(csp); - CHERI_DBG("SANDBOX: SETTING CFP %#p\n", (void *)newFP); - newRegisters.setFP(newFP); - // Get the new return address. - return addressSpace.getP(csp + CI.kPCOffset); -} - -template -bool DwarfInstructions::isCompartmentTransitionTrampoline( - pint_t ecsp, A &addressSpace, CompartmentInfo &CI, pint_t returnAddress) { - ptraddr_t expectedReturnAddress = - addressSpace.template get(ecsp + CI.kReturnAddressOffset); - CHERI_DBG( - "isCompartmentTransitionTrampoline(): expectedReturnAddress: 0x%lx\n", - expectedReturnAddress); - return expectedReturnAddress == returnAddress; -} -#else // _LIBUNWIND_TARGET_AARCH64 -template -typename A::pint_t DwarfInstructions::restoreRegistersFromSandbox( - pint_t csp, A &addressSpace, R &newRegisters, CompartmentInfo &CI, - pint_t sealer) { - assert(0 && "not implemented on this architecture"); - return (pint_t)0; -} -template -bool DwarfInstructions::isCompartmentTransitionTrampoline( - pint_t ecsp, A &addressSpace, CompartmentInfo &CI, pint_t returnAddress) { - assert(0 && "not implemented on this architecture"); - return false; -} -#endif // _LIBUNWIND_TARGET_AARCH64 -#endif // __CHERI_PURE_CAPABILITY__ && _LIBUNWIND_CHERI_C18N_SUPPORT - template int DwarfInstructions::stepWithDwarf(A &addressSpace, pc_t pc, pint_t fdeStart, R ®isters, @@ -484,23 +412,14 @@ int DwarfInstructions::stepWithDwarf(A &addressSpace, pc_t pc, #endif #if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_CHERI_C18N_SUPPORT) - // If the sealer is not valid (only the case when we're running without - // c18n), check if the return address has the executive mode bit set. - // If so, we should be calling into the c18n RTLD as this is a - // compartment boundary. We need to restore registers from the executive - // stack and ask rtld for it. - uintptr_t sealer = addressSpace.getUnwindSealer(); - if (addressSpace.isValidSealer(sealer)) { + // If c18n is enabled, check if we are at a compartment boundary. If so, + // restore registers from the trusted stack by asking rtld for them. + if (CompartInfo.isC18NEnabled()) { pint_t csp = registers.getTrustedStack(); - if (__builtin_cheri_sealed_get(csp)) - csp = __builtin_cheri_unseal(csp, sealer); - CompartmentInfo &CI = CompartmentInfo::sThisCompartmentInfo; - if (csp != 0 && isCompartmentTransitionTrampoline(csp, addressSpace, CI, - returnAddress)) { + if (CompartInfo.isC18NTramp(returnAddress, csp)) { CHERI_DBG("%#p: detected a trampoline, unwinding from sandbox\n", (void *)returnAddress); - returnAddress = restoreRegistersFromSandbox( - csp, addressSpace, newRegisters, CI, sealer); + returnAddress = CompartInfo.fillC18NState(newRegisters, csp); } } #endif diff --git a/contrib/subrepo-cheri-libunwind/src/UnwindRegistersRestore.S b/contrib/subrepo-cheri-libunwind/src/UnwindRegistersRestore.S index 351ef4e3bdbb..b93e7c7b44e1 100644 --- a/contrib/subrepo-cheri-libunwind/src/UnwindRegistersRestore.S +++ b/contrib/subrepo-cheri-libunwind/src/UnwindRegistersRestore.S @@ -714,7 +714,7 @@ DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_arm64_jumpto) #ifdef __CHERI_PURE_CAPABILITY__ ldr c1, [c0, #0x1f0] // Pass the target untrusted stack pointer ldr c2, [c0, #0x210] // Pass the target trusted stack pointer - bl _rtld_unw_setcontext + bl c18n_unwind_trusted_stk // skip restore of c0,c1 for now ldp c2, c3, [c0, #0x020] diff --git a/contrib/subrepo-cheri-libunwind/src/UnwindRegistersSave.S b/contrib/subrepo-cheri-libunwind/src/UnwindRegistersSave.S index bb2dd7cb8bf3..74f07812d834 100644 --- a/contrib/subrepo-cheri-libunwind/src/UnwindRegistersSave.S +++ b/contrib/subrepo-cheri-libunwind/src/UnwindRegistersSave.S @@ -838,16 +838,18 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) #elif defined(__aarch64__) #ifdef __CHERI_PURE_CAPABILITY__ -DEFINE_LIBUNWIND_FUNCTION(__rtld_unw_noop) +DEFINE_LIBUNWIND_FUNCTION(_c18n_noop) #ifdef __ARM_MORELLO_PURECAP_BENCHMARK_ABI and x30, x30, #~1 ret x30 #else ret #endif -END_LIBUNWIND_FUNCTION(__rtld_unw_noop) -WEAK_ALIAS(__rtld_unw_noop, _rtld_unw_getcontext) -WEAK_ALIAS(__rtld_unw_noop, _rtld_unw_setcontext) +END_LIBUNWIND_FUNCTION(_c18n_noop) +// uintptr_t c18n_get_trusted_stk(uintptr_t, struct trusted_frame **); +WEAK_ALIAS(_c18n_noop, c18n_get_trusted_stk) +// uintptr_t c18n_unwind_trusted_stk(uintptr_t, void *, struct trusted_frame *); +WEAK_ALIAS(_c18n_noop, c18n_unwind_trusted_stk) #endif // @@ -902,7 +904,7 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) str d30, [c0, #0x0f0] str d31, [c0, #0x0f8] mov x0, #0 // return UNW_ESUCCESS - b _rtld_unw_getcontext + b c18n_get_trusted_stk #else stp x0, x1, [x0, #0x000] stp x2, x3, [x0, #0x010] diff --git a/contrib/subrepo-cheri-libunwind/src/libunwind.cpp b/contrib/subrepo-cheri-libunwind/src/libunwind.cpp index 4b5748c3bab7..83648894a740 100644 --- a/contrib/subrepo-cheri-libunwind/src/libunwind.cpp +++ b/contrib/subrepo-cheri-libunwind/src/libunwind.cpp @@ -28,7 +28,6 @@ #if !defined(__USING_SJLJ_EXCEPTIONS__) #include "AddressSpace.hpp" -#include "CompartmentInfo.hpp" #include "UnwindCursor.hpp" @@ -43,11 +42,6 @@ using namespace libunwind; /// internal object to represent this processes address space LocalAddressSpace LocalAddressSpace::sThisAddressSpace; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_CHERI_C18N_SUPPORT) -/// internal object to represent this processes compartment information -CompartmentInfo CompartmentInfo::sThisCompartmentInfo; -#endif // __CHERI_PURE_CAPABILITY__ && _LIBUNWIND_CHERI_C18N_SUPPORT - _LIBUNWIND_EXPORT unw_addr_space_t unw_local_addr_space = (unw_addr_space_t)&LocalAddressSpace::sThisAddressSpace; From 157b131c48cf1f7621f370a34ad44d457f5bc754 Mon Sep 17 00:00:00 2001 From: Dapeng Gao Date: Thu, 4 Jul 2024 11:50:31 +0100 Subject: [PATCH 06/12] riscv: Add sysarch(2) support for utidc --- sys/riscv/include/frame.h | 1 + sys/riscv/include/reg.h | 1 + sys/riscv/include/sysarch.h | 2 ++ sys/riscv/include/ucontext.h | 1 + sys/riscv/riscv/exception.S | 13 +++++++++++++ sys/riscv/riscv/exec_machdep.c | 8 ++++++-- sys/riscv/riscv/genassym.c | 1 + sys/riscv/riscv/swtch.S | 3 +++ sys/riscv/riscv/sys_machdep.c | 9 +++++++++ 9 files changed, 37 insertions(+), 2 deletions(-) diff --git a/sys/riscv/include/frame.h b/sys/riscv/include/frame.h index 4a28fd62f763..86ecb38c7e9a 100644 --- a/sys/riscv/include/frame.h +++ b/sys/riscv/include/frame.h @@ -54,6 +54,7 @@ struct trapframe { uintcap_t tf_a[8]; uintcap_t tf_sepc; uintcap_t tf_ddc; + uintcap_t tf_tidc; #else uint64_t tf_ra; uint64_t tf_sp; diff --git a/sys/riscv/include/reg.h b/sys/riscv/include/reg.h index 5883e24c51d2..8355f2e1953e 100644 --- a/sys/riscv/include/reg.h +++ b/sys/riscv/include/reg.h @@ -69,6 +69,7 @@ struct capreg { __uintcap_t ca[8]; /* function arguments */ __uintcap_t sepcc; /* exception program counter */ __uintcap_t ddc; + __uintcap_t tidc; __uint64_t tagmask; __uint64_t pad; }; diff --git a/sys/riscv/include/sysarch.h b/sys/riscv/include/sysarch.h index 8cf1243b57bb..19b2887a343e 100644 --- a/sys/riscv/include/sysarch.h +++ b/sys/riscv/include/sysarch.h @@ -43,6 +43,8 @@ #define QEMU_SET_QTRACE 101 /* Set (or clear) QEMU tracing. */ #define QEMU_SET_QTRACE_USER 102 /* Set (or clear) QEMU per-thread user-only tracing mode */ +#define RISCV_SET_UTIDC 103 /* Set user thread id capability */ + #ifndef _KERNEL __BEGIN_DECLS diff --git a/sys/riscv/include/ucontext.h b/sys/riscv/include/ucontext.h index 7352c4abda4b..15020eeb91d8 100644 --- a/sys/riscv/include/ucontext.h +++ b/sys/riscv/include/ucontext.h @@ -65,6 +65,7 @@ struct capregs { __uintcap_t cp_ca[8]; __uintcap_t cp_sepcc; __uintcap_t cp_ddc; + __uintcap_t cp_tidc; __register_t cp_sstatus; __register_t cp_pad; }; diff --git a/sys/riscv/riscv/exception.S b/sys/riscv/riscv/exception.S index a1f718a40075..bb9b74cb3b06 100644 --- a/sys/riscv/riscv/exception.S +++ b/sys/riscv/riscv/exception.S @@ -126,6 +126,13 @@ cspecialw sscratchc, ct0 cspecialr ct0, sepcc csc ct0, (TF_SEPC)(csp) + /* Store user tidc */ +.if \mode == 1 + cspecialr ct0, 11 /* stidc */ +.else + cspecialr ct0, 3 /* utidc */ +.endif + csc ct0, (TF_TIDC)(csp) cspecialr ct0, ddc csc ct0, (TF_DDC)(csp) @@ -280,6 +287,12 @@ */ clc ct0, (TF_DDC)(csp) cspecialw ddc, ct0 + clc ct0, (TF_TIDC)(csp) +.if \mode == 1 + cspecialw 11, ct0 /* stidc */ +.else + cspecialw 3, ct0 /* utidc */ +.endif clc ct0, (TF_SEPC)(csp) cspecialw sepcc, ct0 diff --git a/sys/riscv/riscv/exec_machdep.c b/sys/riscv/riscv/exec_machdep.c index 6657c6b71a52..35a58afc2c0c 100644 --- a/sys/riscv/riscv/exec_machdep.c +++ b/sys/riscv/riscv/exec_machdep.c @@ -72,8 +72,8 @@ static void get_fpcontext(struct thread *td, mcontext_t *mcp); static void set_fpcontext(struct thread *td, mcontext_t *mcp); #if __has_feature(capabilities) -_Static_assert(sizeof(mcontext_t) == 1152, "mcontext_t size incorrect"); -_Static_assert(sizeof(ucontext_t) == 1248, "ucontext_t size incorrect"); +_Static_assert(sizeof(mcontext_t) == 1168, "mcontext_t size incorrect"); +_Static_assert(sizeof(ucontext_t) == 1264, "ucontext_t size incorrect"); _Static_assert(sizeof(siginfo_t) == 112, "siginfo_t size incorrect"); #else _Static_assert(sizeof(mcontext_t) == 864, "mcontext_t size incorrect"); @@ -235,6 +235,8 @@ _Static_assert(offsetof(struct trapframe, tf_sepc) == offsetof(struct capreg, sepcc), "sepcc mismatch"); _Static_assert(offsetof(struct trapframe, tf_ddc) == offsetof(struct capreg, ddc), "ddc mismatch"); +_Static_assert(offsetof(struct trapframe, tf_tidc) == + offsetof(struct capreg, tidc), "tidc mismatch"); int fill_capregs(struct thread *td, struct capreg *regs) @@ -418,6 +420,7 @@ get_mcontext(struct thread *td, mcontext_t *mcp, int clear_ret) mcp->mc_capregs.cp_ctp = tf->tf_tp; mcp->mc_capregs.cp_sepcc = tf->tf_sepc; mcp->mc_capregs.cp_ddc = tf->tf_ddc; + mcp->mc_capregs.cp_tidc = tf->tf_tidc; mcp->mc_capregs.cp_sstatus = tf->tf_sstatus; #else memcpy(mcp->mc_gpregs.gp_t, tf->tf_t, sizeof(mcp->mc_gpregs.gp_t)); @@ -478,6 +481,7 @@ set_mcontext(struct thread *td, mcontext_t *mcp) tf->tf_gp = mcp->mc_capregs.cp_cgp; tf->tf_sepc = mcp->mc_capregs.cp_sepcc; tf->tf_ddc = mcp->mc_capregs.cp_ddc; + tf->tf_tidc = mcp->mc_capregs.cp_tidc; tf->tf_sstatus = mcp->mc_capregs.cp_sstatus; #else memcpy(tf->tf_t, mcp->mc_gpregs.gp_t, sizeof(tf->tf_t)); diff --git a/sys/riscv/riscv/genassym.c b/sys/riscv/riscv/genassym.c index 7ed53ddbc4d8..1c9aa4abf3b2 100644 --- a/sys/riscv/riscv/genassym.c +++ b/sys/riscv/riscv/genassym.c @@ -99,6 +99,7 @@ ASSYM(TF_A, offsetof(struct trapframe, tf_a)); ASSYM(TF_SEPC, offsetof(struct trapframe, tf_sepc)); #if __has_feature(capabilities) ASSYM(TF_DDC, offsetof(struct trapframe, tf_ddc)); +ASSYM(TF_TIDC, offsetof(struct trapframe, tf_tidc)); #endif ASSYM(TF_STVAL, offsetof(struct trapframe, tf_stval)); ASSYM(TF_SCAUSE, offsetof(struct trapframe, tf_scause)); diff --git a/sys/riscv/riscv/swtch.S b/sys/riscv/riscv/swtch.S index 45d460fb4677..36d3d64128aa 100644 --- a/sys/riscv/riscv/swtch.S +++ b/sys/riscv/riscv/swtch.S @@ -682,6 +682,9 @@ ENTRY(fork_trampoline) clc ct0, (TF_DDC)(csp) cspecialw ddc, ct0 + clc ct0, (TF_TIDC)(csp) + cspecialw 3, ct0 /* utidc */ + /* Restore exception program counter */ clc ct0, (TF_SEPC)(csp) cspecialw sepcc, ct0 diff --git a/sys/riscv/riscv/sys_machdep.c b/sys/riscv/riscv/sys_machdep.c index f6d7d56f1a86..75fa8a8ac127 100644 --- a/sys/riscv/riscv/sys_machdep.c +++ b/sys/riscv/riscv/sys_machdep.c @@ -71,6 +71,15 @@ sysarch(struct thread *td, struct sysarch_args *uap) else td->td_md.md_flags &= ~MDTD_QTRACE_USERMODE; return (0); +#endif +#if __has_feature(capabilities) + case RISCV_SET_UTIDC: { + uintcap_t val; + int error = copyincap(uap->parms, &val, sizeof(val)); + if (!error) + td->td_frame->tf_tidc = val; + return (error); + } #endif default: break; From 2d6ce29a5e338a95cc6298a8feaba70e6181a30a Mon Sep 17 00:00:00 2001 From: Dapeng Gao Date: Wed, 10 Jul 2024 10:39:45 +0100 Subject: [PATCH 07/12] c18n: Clear correct number of return values during tail-call In the following example, bar makes a tail-call to foo, which returns a value that is observable to the caller of bar, even though bar returns nothing. void *foo(); void bar() { foo(); } When bar is called, previous versions of the trampoline clears return value registers as if foo is being called directly, leaking a capability. Instead, clear the maximum number of return value registers as required for both foo and bar. --- libexec/rtld-elf/aarch64/rtld_c18n_asm.S | 150 ++++++++++--------- libexec/rtld-elf/aarch64/rtld_c18n_machdep.c | 98 ++---------- libexec/rtld-elf/rtld_c18n.h | 5 +- 3 files changed, 95 insertions(+), 158 deletions(-) diff --git a/libexec/rtld-elf/aarch64/rtld_c18n_asm.S b/libexec/rtld-elf/aarch64/rtld_c18n_asm.S index f0b809f24754..edc8d6fb5430 100644 --- a/libexec/rtld-elf/aarch64/rtld_c18n_asm.S +++ b/libexec/rtld-elf/aarch64/rtld_c18n_asm.S @@ -104,8 +104,8 @@ ENTRY(create_untrusted_stk) /* * NON-STANDARD CALLING CONVENTION * - * c19: Callee to be tail-called - * w20: Callee's compartment ID + * w19: Callee's compartment ID + * c26: Callee to be tail-called * * The function resolves the callee's stack, installs it, and tail-calls * the callee. @@ -123,7 +123,7 @@ ENTRY(create_untrusted_stk) save_arguments - mov w0, w20 + mov w0, w19 bl resolve_untrusted_stk_impl mov c10, c0 @@ -145,14 +145,14 @@ ENTRY(create_untrusted_stk) mov x18, xzr /* - * All callee-saved registers are safe except c23 + * All callee-saved registers are safe except c28 */ - mov x23, xzr + mov x28, xzr #ifdef __ARM_MORELLO_PURECAP_BENCHMARK_ABI - br x19 + br x26 #else - brr c19 + brr c26 #endif END(create_untrusted_stk) @@ -236,85 +236,95 @@ TRAMP(tramp_push_frame) * Store the caller's current stack top in the stack lookup table. */ str c15, [STACK_TABLE_C, w12, uxtw #0] - - stp c19, c20, [TRUSTED_STACK_C, #(-CAP_WIDTH * TRUSTED_FRAME_SIZE + CAP_WIDTH * 2)] - /* - * Load the target capability. - */ -1: ldr c19, #0 /* To be patched at runtime */ - /* - * Get the callee's compartment ID. - */ -2: movz w20, #0 /* To be patched at runtime */ /* * Get the length of the stack lookup table. */ gclen x13, STACK_TABLE_C + + stp c19, c20, [TRUSTED_STACK_C, #(-CAP_WIDTH * TRUSTED_FRAME_SIZE + CAP_WIDTH * 2)] /* - * Use subs instead of cmp to clear a register tag. + * Get the callee's compartment ID. */ - subs x14, x13, x20 - +1: movz w19, #0 /* To be patched at runtime */ /* - * Save the callee's current stack top and old stack top. + * Use subs instead of cmp to clear a capability tag. */ - stp c15, c16, [TRUSTED_STACK_C, #(-CAP_WIDTH * TRUSTED_FRAME_SIZE + TRUSTED_FRAME_SP_OSP)] + subs x14, x13, x19 /* * If the stack lookup table index is out-of-bounds, set it to zero. */ - csel w16, w20, wzr, hi + csel w20, w19, wzr, hi /* * Load the callee's stack if the stack lookup table index is within * bounds. Otherwise the resolver will be loaded. */ - ldr c17, [STACK_TABLE_C, w16, uxtw #0] + ldr c17, [STACK_TABLE_C, w20, uxtw #0] + /* + * The tag of the return capability is set iff the condition flag is cs. + */ + chktgd c30 + /* + * Compare the return address to the landing address. The call is a + * tail-call iff the condition flag is eq. + */ + ccmp x30, x11, #0, cs stp c21, c22, [TRUSTED_STACK_C, #(-CAP_WIDTH * TRUSTED_FRAME_SIZE + CAP_WIDTH * 4)] /* - * The resolver is loaded iff the condition flag is ne. + * Get the offset to the next trusted frame. */ - gcperm x21, c17 - ands x22, x21, #(1 << 15) + mov x21, #-(CAP_WIDTH * TRUSTED_FRAME_SIZE) /* - * If the resolver is loaded, keep the stack unchanged. Otherwise, - * install the callee's stack. + * If the call is a tail-call, do not bump the trusted stack pointer. */ - csel c15, c15, c17, ne + csel x22, xzr, x21, eq stp c23, c24, [TRUSTED_STACK_C, #(-CAP_WIDTH * TRUSTED_FRAME_SIZE + CAP_WIDTH * 6)] /* - * If the resolver is loaded, set the branch target to it. Otherwise, - * install the callee. + * If the call is a tail-call, get the number of return value registers + * of the caller. */ - csel c23, c17, c19, ne + csinv x23, x10, xzr, eq /* - * Compare the return address to the landing address. + * Get the landing address. */ - subs x24, x30, x11 +2: adr c24, #0 /* To be patched at runtime */ stp c25, c26, [TRUSTED_STACK_C, #(-CAP_WIDTH * TRUSTED_FRAME_SIZE + CAP_WIDTH * 8)] /* - * Get the tag of the return capability. + * Compute the number of return value registers. If the call is a tail- + * call, it is the minimum of that of the caller and the callee. */ - gctag x25, c30 +3: ubfm x25, x23, #48, #0 /* To be patched at runtime */ /* - * Get the offset to the next trusted frame. + * Load the target capability. */ - mov x26, #-(CAP_WIDTH * TRUSTED_FRAME_SIZE) +4: ldr c26, #0 /* To be patched at runtime */ + + /* + * Save the caller's current stack top and old stack top. + */ + stp c15, c16, [TRUSTED_STACK_C, #(-CAP_WIDTH * TRUSTED_FRAME_SIZE + TRUSTED_FRAME_SP_OSP)] /* - * The call is a tail-call iff the condition flag is eq. + * Get the permissions of the loaded value. */ - ccmp x25, #1, #0, eq + gcperm x16, c17 stp c27, c28, [TRUSTED_STACK_C, #(-CAP_WIDTH * TRUSTED_FRAME_SIZE + CAP_WIDTH * 10)] /* - * If the call is a tail call, do not bump the trusted stack pointer. + * The resolver is loaded iff the condition flag is ne. */ - csel x27, xzr, x26, eq + ands x27, x16, #(1 << 15) /* - * Get the landing address. + * If the resolver is loaded, keep the stack unchanged. Otherwise, + * install the callee's stack. + */ + csel c15, c15, c17, ne + /* + * If the resolver is loaded, set the branch target to it. Otherwise, + * install the callee. */ -3: adr c28, #0 /* To be patched at runtime */ + csel c28, c17, c26, ne /* * Save the address of the previous trusted frame and the compartment ID @@ -330,21 +340,22 @@ TRAMP(tramp_push_frame) * information about the callee regardless of whether the call is a * tail-call. */ - add TRUSTED_STACK_C, TRUSTED_STACK_C, x27 + add TRUSTED_STACK_C, TRUSTED_STACK_C, x22 /* * Save the landing address. */ - str x28, [TRUSTED_STACK_C, #TRUSTED_FRAME_LANDING] + str x24, [TRUSTED_STACK_C, #TRUSTED_FRAME_LANDING] /* - * Get the number of return value registers. + * Combine the caller's compartment ID and the number of return value + * registers. */ -4: add w28, w20, #0, lsl #12 /* To be patched at runtime */ + orr w24, w19, w25, lsl #16 /* * Save the callee's compartment ID and the number of return value * registers. */ - str w28, [TRUSTED_STACK_C, #TRUSTED_FRAME_CALLEE] + str w24, [TRUSTED_STACK_C, #TRUSTED_FRAME_CALLEE] msr TRUSTED_STACK, TRUSTED_STACK_C @@ -358,10 +369,10 @@ TRAMP(tramp_push_frame) set_untrusted_stk c15 TRAMPEND(tramp_push_frame) -PATCH_POINT(tramp_push_frame, target, 1b) -PATCH_POINT(tramp_push_frame, cid, 2b) -PATCH_POINT(tramp_push_frame, landing, 3b) -PATCH_POINT(tramp_push_frame, ret_args, 4b) +PATCH_POINT(tramp_push_frame, cid, 1b) +PATCH_POINT(tramp_push_frame, landing, 2b) +PATCH_POINT(tramp_push_frame, n_rets, 3b) +PATCH_POINT(tramp_push_frame, target, 4b) /* * Save the address of the current frame to c29 so that unwinders can locate it. @@ -377,6 +388,7 @@ TRAMPEND(tramp_update_fp_untagged) TRAMP(tramp_count_entry) 1: ldr c24, #0 /* To be patched at runtime */ + movz w25, #1 stadd w25, [c24] TRAMPEND(tramp_count_entry) @@ -400,9 +412,9 @@ PATCH_POINT(tramp_call_hook, header, 3b) TRAMP(tramp_invoke_exe) #ifdef __ARM_MORELLO_PURECAP_BENCHMARK_ABI - blr x23 + blr x28 #else - blr c23 + blr c28 #endif TRAMPEND(tramp_invoke_exe) @@ -429,9 +441,9 @@ TRAMP(tramp_invoke_res) clrtag TRUSTED_STACK_C, TRUSTED_STACK_C #ifdef __ARM_MORELLO_PURECAP_BENCHMARK_ABI - blr x23 + blr x28 #else - blrr c23 + blrr c28 #endif TRAMPEND(tramp_invoke_res) @@ -486,22 +498,22 @@ TRAMP(tramp_pop_frame) str c12, [STACK_TABLE_C, w10, uxtw #0] /* - * Extract the number of return value registers. + * Extract the number of return value registers. The number of return + * value registers is encoded as follows: + * - TWO: 0b11 + * - ONE: 0b01 + * - NONE: 0b00 + * - INDIRECT: 0b00 */ - ubfx x13, x10, #48, #2 + ubfx x13, x10, #50, #2 /* - * Clear unused return value registers. The registers to clear are - * encoded as follows: - * - None: 0b00 - * - c1 only: 0b01 - * - c0 and c1: 0b1x - * Use comparison and csel to avoid branching. + * Clear unused return value registers. * - * Use subs instead of cmp to clear a register tag. + * Use subs instead of cmp to clear a capability tag. */ subs w14, w13, #0b01 - csel c0, czr, c0, hi - csel c1, czr, c1, hs + csel c0, czr, c0, lo + csel c1, czr, c1, ls /* * Clear temporary registers. diff --git a/libexec/rtld-elf/aarch64/rtld_c18n_machdep.c b/libexec/rtld-elf/aarch64/rtld_c18n_machdep.c index 729b1b5facb8..b0b0e958065f 100644 --- a/libexec/rtld-elf/aarch64/rtld_c18n_machdep.c +++ b/libexec/rtld-elf/aarch64/rtld_c18n_machdep.c @@ -100,10 +100,10 @@ tramp_compile(char **entry, const struct tramp_data *data) *PATCH_INS(PATCH_OFF(tramp, name)) |= _value; \ } while (0) -#define PATCH_ADD(tramp, name, value) \ +#define PATCH_UBFM(tramp, name, value) \ do { \ uint32_t _value = (value); \ - _value = ((_value & 0xfff) << 10); \ + _value = ((_value & 0x3f) << 10); \ *PATCH_INS(PATCH_OFF(tramp, name)) |= _value; \ } while (0) @@ -145,11 +145,18 @@ tramp_compile(char **entry, const struct tramp_data *data) size += offsetof(struct tramp_header, entry); COPY(push_frame); - PATCH_LDR_IMM(push_frame, target, target_off); PATCH_MOV(push_frame, cid, cid_to_index(data->defobj->compart_id).val); - PATCH_ADD(push_frame, ret_args, - data->sig.valid ? data->sig.ret_args << (16 - 12) : 0); landing_off = PATCH_OFF(push_frame, landing); + /* + * The number of return value registers is encoded as follows: + * - TWO: 0b1111 + * - ONE: 0b0111 + * - NONE: 0b0011 + * - INDIRECT: 0b0001 + */ + PATCH_UBFM(push_frame, n_rets, + 51 - (data->sig.valid ? data->sig.ret_args : 0)); + PATCH_LDR_IMM(push_frame, target, target_off); if (executive || ld_compartment_unwind != NULL) COPY(update_fp); @@ -206,84 +213,3 @@ tramp_compile(char **entry, const struct tramp_data *data) return (size); } - -/* - * APIs - */ -void * -_rtld_safebox_code(void *target, struct func_sig sig) -{ - const Obj_Entry *obj; - - if (!func_sig_legal(sig)) { - _rtld_error( - "_rtld_sandbox_code: Invalid signature " - C18N_SIG_FORMAT_STRING, - C18N_SIG_FORMAT(sig)); - return (NULL); - } - - if ((cheri_getperm(target) & CHERI_PERM_EXECUTIVE) != 0) - return (target); - - obj = obj_from_addr(target); - if (obj == NULL) { - _rtld_error( - "_rtld_sandbox_code: " - "%#p does not belong to any object", target); - return (NULL); - } - - if (sig.valid) { - asm ("chkssu %0, %0, %1" - : "+C" (target) - : "C" (obj->text_rodata_cap) - : "cc"); - target = cheri_seal(target, - sealer_tramp + func_sig_to_otype(sig)); - } - - return (target); -} - -void * -_rtld_sandbox_code(void *target, struct func_sig sig) -{ - const Obj_Entry *obj; - void *target_unsealed; - - if (!func_sig_legal(sig)) { - _rtld_error( - "_rtld_sandbox_code: Invalid signature " - C18N_SIG_FORMAT_STRING, - C18N_SIG_FORMAT(sig)); - return (NULL); - } - - if ((cheri_getperm(target) & CHERI_PERM_EXECUTIVE) != 0) - return (target); - - obj = obj_from_addr(target); - if (obj == NULL) { - _rtld_error( - "_rtld_sandbox_code: " - "%#p does not belong to any object", target); - return (NULL); - } - - target_unsealed = cheri_unseal(target, sealer_tramp); - if (cheri_gettag(target_unsealed)) { - if (sig.valid && cheri_gettype(target) != - (long)cheri_getbase(sealer_tramp) + func_sig_to_otype(sig)) - rtld_fatal("Signature mismatch"); - target = cheri_sealentry(target_unsealed); - } - - target = tramp_intern(NULL, &(struct tramp_data) { - .target = target, - .defobj = obj, - .sig = sig - }); - - return (target); -} diff --git a/libexec/rtld-elf/rtld_c18n.h b/libexec/rtld-elf/rtld_c18n.h index e3aefcd3cdde..9fa00d99d02e 100644 --- a/libexec/rtld-elf/rtld_c18n.h +++ b/libexec/rtld-elf/rtld_c18n.h @@ -150,10 +150,9 @@ struct trusted_frame { */ stk_table_index callee; /* - * Number of return value registers, encoded in enum tramp_ret_args + * Number of return value registers with architecture-specific encoding */ - uint8_t ret_args : 2; - uint16_t reserved : 14; + uint16_t n_rets; /* * This field contains the code address in the trampoline that the * callee should return to. This is used by trampolines to detect cross- From 3e81399774bc47aaaa519e0cb19903052d5a6242 Mon Sep 17 00:00:00 2001 From: Dapeng Gao Date: Tue, 16 Jul 2024 17:34:14 +0100 Subject: [PATCH 08/12] c18n: Support compartmentalising RTLD on RISC-V --- libexec/rtld-elf/Makefile | 2 +- libexec/rtld-elf/cheri/cheri_reloc.h | 8 + libexec/rtld-elf/map_object.c | 2 +- libexec/rtld-elf/riscv/rtld_c18n_asm.S | 538 +++++++++++++++++++++ libexec/rtld-elf/riscv/rtld_c18n_machdep.c | 156 ++++++ libexec/rtld-elf/riscv/rtld_c18n_machdep.h | 140 ++++++ libexec/rtld-elf/riscv/rtld_machdep.h | 18 + libexec/rtld-elf/riscv/rtld_start.S | 4 + libexec/rtld-elf/rtld.c | 4 +- libexec/rtld-elf/rtld_c18n.c | 89 +++- libexec/rtld-elf/rtld_c18n.h | 13 +- libexec/rtld-elf/rtld_c18n_mi.S | 2 +- 12 files changed, 955 insertions(+), 21 deletions(-) create mode 100644 libexec/rtld-elf/riscv/rtld_c18n_asm.S create mode 100644 libexec/rtld-elf/riscv/rtld_c18n_machdep.c create mode 100644 libexec/rtld-elf/riscv/rtld_c18n_machdep.h diff --git a/libexec/rtld-elf/Makefile b/libexec/rtld-elf/Makefile index 87406ded2bea..c977b970cf21 100644 --- a/libexec/rtld-elf/Makefile +++ b/libexec/rtld-elf/Makefile @@ -29,7 +29,7 @@ MK_UBSAN= no .include -.if ${MACHINE_ABI:Mpurecap} && ${MACHINE_CPUARCH} == "aarch64" +.if ${MACHINE_ABI:Mpurecap} && (${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "riscv") CHERI_LIB_C18N= .endif diff --git a/libexec/rtld-elf/cheri/cheri_reloc.h b/libexec/rtld-elf/cheri/cheri_reloc.h index 950d80fabb86..b432ba49dcac 100644 --- a/libexec/rtld-elf/cheri/cheri_reloc.h +++ b/libexec/rtld-elf/cheri/cheri_reloc.h @@ -125,6 +125,14 @@ process_r_cheri_capability(Obj_Entry *obj, Elf_Word r_symndx, symname(obj, r_symndx), obj->path); return -1; } +#if defined(CHERI_LIB_C18N) && defined(__riscv) + symval = tramp_intern(NULL, &(struct tramp_data) { + .target = __DECONST(void *, symval), + .defobj = defobj, + .def = def, + .sig = sigtab_get(obj, r_symndx) + }); +#endif } else { /* Remove execute permissions and set bounds */ symval = cheri_incoffset(make_data_cap(def, defobj), addend); diff --git a/libexec/rtld-elf/map_object.c b/libexec/rtld-elf/map_object.c index 3865d872c374..87d042ed4eab 100644 --- a/libexec/rtld-elf/map_object.c +++ b/libexec/rtld-elf/map_object.c @@ -278,7 +278,7 @@ map_object(int fd, const char *path, const struct stat *sb, const char* main_pat base_addr, mapsize, PROT_NONE | PROT_MAX(_PROT_ALL), base_flags); mapbase = mmap(base_addr, mapsize, PROT_NONE | PROT_MAX(_PROT_ALL), base_flags, -1, 0); -#ifdef CHERI_LIB_C18N +#ifdef HAS_RESTRICTED_MODE mapbase = cheri_clearperm(mapbase, c18n_code_perm_clear); #endif if (mapbase == MAP_FAILED) { diff --git a/libexec/rtld-elf/riscv/rtld_c18n_asm.S b/libexec/rtld-elf/riscv/rtld_c18n_asm.S new file mode 100644 index 000000000000..f2834fa58bd2 --- /dev/null +++ b/libexec/rtld-elf/riscv/rtld_c18n_asm.S @@ -0,0 +1,538 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2024 Dapeng Gao + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#define IN_ASM +#include "rtld_c18n_machdep.h" +#undef IN_ASM + +/* + * See rtld_c18n.h for an overview of the design. + */ + +/* + * The _rtld_sighandler function is the actual signal handler passed to the + * kernel when the user calls sigaction. It dispatches the signal to the + * appropriate handler registered by the user. + */ +ENTRY(_rtld_sighandler) + /* + * Get the interrupted compartment's current stack top. + */ + get_untrusted_stk ca3 + /* + * The function is executing on an unknown untrusted stack. Switch to + * RTLD's stack. + */ + get_rtld_stk ca4 + /* + * Allocate a sigframe on RTLD's stack. + */ + cincoffsetimm ca4, ca4, -SIG_FRAME_SIZE + set_untrusted_stk ca4 + j _rtld_sighandler_impl +END(_rtld_sighandler) + +.macro save_arguments + cincoffsetimm csp, csp, -(9 * CLEN_BYTES + 8 * 8) + + /* Save floating point arguments */ + cfsd fa0, (0 * 8)(csp) + cfsd fa1, (1 * 8)(csp) + cfsd fa2, (2 * 8)(csp) + cfsd fa3, (3 * 8)(csp) + cfsd fa4, (4 * 8)(csp) + cfsd fa5, (5 * 8)(csp) + cfsd fa6, (6 * 8)(csp) + cfsd fa7, (7 * 8)(csp) + + /* Save argument registers */ + csc ca0, (0 * CLEN_BYTES + 8 * 8)(csp) + csc ca1, (1 * CLEN_BYTES + 8 * 8)(csp) + csc ca2, (2 * CLEN_BYTES + 8 * 8)(csp) + csc ca3, (3 * CLEN_BYTES + 8 * 8)(csp) + csc ca4, (4 * CLEN_BYTES + 8 * 8)(csp) + csc ca5, (5 * CLEN_BYTES + 8 * 8)(csp) + csc ca6, (6 * CLEN_BYTES + 8 * 8)(csp) + csc ca7, (7 * CLEN_BYTES + 8 * 8)(csp) + + /* Save return address */ + csc cra, (8 * CLEN_BYTES + 8 * 8)(csp) +.endmacro + +.macro restore_arguments + /* Restore floating point arguments */ + cfsd fa0, (0 * 8)(csp) + cfsd fa1, (1 * 8)(csp) + cfsd fa2, (2 * 8)(csp) + cfsd fa3, (3 * 8)(csp) + cfsd fa4, (4 * 8)(csp) + cfsd fa5, (5 * 8)(csp) + cfsd fa6, (6 * 8)(csp) + cfsd fa7, (7 * 8)(csp) + + /* Restore argument registers */ + clc ca0, (0 * CLEN_BYTES + 8 * 8)(csp) + clc ca1, (1 * CLEN_BYTES + 8 * 8)(csp) + clc ca2, (2 * CLEN_BYTES + 8 * 8)(csp) + clc ca3, (3 * CLEN_BYTES + 8 * 8)(csp) + clc ca4, (4 * CLEN_BYTES + 8 * 8)(csp) + clc ca5, (5 * CLEN_BYTES + 8 * 8)(csp) + clc ca6, (6 * CLEN_BYTES + 8 * 8)(csp) + clc ca7, (7 * CLEN_BYTES + 8 * 8)(csp) + + /* Restore return address */ + clc cra, (8 * CLEN_BYTES + 8 * 8)(csp) + + cincoffsetimm csp, csp, (9 * CLEN_BYTES + 8 * 8) +.endmacro + +/* + * The create_untrusted_stack function has non-standard ABI and is only called + * by trampolines when the destination compartment's stack has not been + * allocated yet. + */ +ENTRY(create_untrusted_stk) + /* + * NON-STANDARD CALLING CONVENTION + * + * s8: Callee to be tail-called + * s9: Callee's compartment ID + * + * The function resolves the callee's stack, installs it, and tail-calls + * the callee. + * + * All argument registers must be preserved. All temporary registers and + * callee-saved registers must be cleared. + */ + + /* + * The execution stack is still the caller's stack. Switch to RTLD's + * stack. + */ + get_rtld_stk ct0 + set_untrusted_stk ct0 + + save_arguments + + move a0, s9 + cjal resolve_untrusted_stk_impl + cmove ct0, ca0 + + restore_arguments + + set_untrusted_stk ct0 + + /* + * Clear temporary registers, except: + * t0: Callee's stack + */ + move t1, x0 + move t2, x0 + move t3, x0 + move t4, x0 + move t5, x0 + move t6, x0 + + /* + * All callee-saved registers are safe except s4 + */ + move s4, x0 + + cjr cs8 +END(create_untrusted_stk) + +/* + * The trampoline templates are assembly code sequences used to construct + * trampolines by tramp_compile. They are code but reside in rodata. Hence a new + * macro is defined to describe them. + */ +#define TRAMP(sym) \ + .section .rodata; .globl sym; .type sym,%object; sym: + +#define TRAMPEND(sym) \ + end_##sym: \ + EEND(sym); \ + .section .rodata; .globl size_##sym; .align 3; \ + .type size_##sym,%object; .size size_##sym, 8; size_##sym: \ + .quad end_##sym - sym + +#define PATCH_POINT(tramp, name, label) \ + .section .rodata; .globl patch_##tramp##_##name; .align 2; \ + .type patch_##tramp##_##name,%object; \ + .size patch_##tramp##_##name, 4; patch_##tramp##_##name: \ + .word label - end_##tramp + +#define TRUSTED_STACK_C ct3 + +TRAMP(tramp_push_frame) +1: auipcc ct0, 0 + /* + * Get the unsealer for the thread identifier buffer. + */ +.option push +.option norvc +2: clc ct1, 0(ct0) /* To be patched at runtime */ +.option pop + /* + * Get and unseal the thread identifier buffer. + */ + cspecialr ct2, TIDC + cunseal ct1, ct2, ct1 + /* + * Get the trusted stack. + */ + clc TRUSTED_STACK_C, TRUSTED_STACK(ct1) + /* + * Load the caller's compartment ID from the previous trusted frame. + */ + clhu t4, TRUSTED_FRAME_CALLEE(TRUSTED_STACK_C) + /* + * Get the stack lookup table. + */ + clc STACK_TABLE_C, STACK_TABLE(ct1) + /* + * Load the caller's old stack top from the stack lookup table. + */ + csetoffset STACK_TABLE_C, STACK_TABLE_C, t4 + clc ct5, 0(STACK_TABLE_C) + /* + * Store the caller's current stack top in the stack lookup table. + */ + csc csp, 0(STACK_TABLE_C) + + csc cs11, (-CLEN_BYTES * TRUSTED_FRAME_SIZE + CLEN_BYTES * 12)(TRUSTED_STACK_C) + /* + * Get the length of the stack lookup table. + */ + cgetlen s11, STACK_TABLE_C + + csc cs10, (-CLEN_BYTES * TRUSTED_FRAME_SIZE + CLEN_BYTES * 11)(TRUSTED_STACK_C) + /* + * Get the callee's compartment ID. + */ +.option push +.option norvc +3: addi s10, x0, 0 /* To be patched at runtime */ +.option pop + csc cs9, (-CLEN_BYTES * TRUSTED_FRAME_SIZE + CLEN_BYTES * 10)(TRUSTED_STACK_C) + addi s9, s10, 0 + /* + * Compare the compartment ID to the length of the stack lookup table. + * If the stack lookup table index is out-of-bounds, set it to zero. + */ + bltu s10, s11, 4f + addi s10, x0, 0 +4: + /* + * Load the callee's stack if the stack lookup table index is within + * bounds. Otherwise the resolver will be loaded. + */ + csetoffset STACK_TABLE_C, STACK_TABLE_C, s10 + clc ct6, 0(STACK_TABLE_C) + + /* + * Load the target capability. + */ + csc cs8, (-CLEN_BYTES * TRUSTED_FRAME_SIZE + CLEN_BYTES * 9)(TRUSTED_STACK_C) +.option push +.option norvc +5: clc cs8, 0(ct0) /* To be patched at runtime */ +.option pop + /* + * Get the permissions of the loaded value. + */ + csc cs7, (-CLEN_BYTES * TRUSTED_FRAME_SIZE + CLEN_BYTES * 8)(TRUSTED_STACK_C) + cgetperm s7, ct6 + /* + * The resolver is loaded iff the value is non-zero. + */ + csc cs6, (-CLEN_BYTES * TRUSTED_FRAME_SIZE + CLEN_BYTES * 7)(TRUSTED_STACK_C) + andi s6, s7, (1 << 1) + /* + * If the resolver is loaded, keep the stack unchanged. Otherwise, + * install the callee's stack. + */ + csc cs5, (-CLEN_BYTES * TRUSTED_FRAME_SIZE + CLEN_BYTES * 6)(TRUSTED_STACK_C) + cmove cs5, ct6 + /* + * If the resolver is loaded, set the branch target to it. Otherwise, + * install the callee. + */ + csc cs4, (-CLEN_BYTES * TRUSTED_FRAME_SIZE + CLEN_BYTES * 5)(TRUSTED_STACK_C) + cmove cs4, cs8 + beqz s6, 6f + cmove cs5, csp + cmove cs4, ct6 +6: + + csc cs3, (-CLEN_BYTES * TRUSTED_FRAME_SIZE + CLEN_BYTES * 4)(TRUSTED_STACK_C) + /* + * Load the landing address from the previous trusted frame. + */ + cld s3, TRUSTED_FRAME_LANDING(TRUSTED_STACK_C) + csc cs2, (-CLEN_BYTES * TRUSTED_FRAME_SIZE + CLEN_BYTES * 3)(TRUSTED_STACK_C) + /* + * Load the existing number of return value registers. + */ + clbu s2, (TRUSTED_FRAME_CALLEE + 2)(TRUSTED_STACK_C) + /* + * Get the tag of the return capability. + */ + csc cs1, (-CLEN_BYTES * TRUSTED_FRAME_SIZE + CLEN_BYTES * 2)(TRUSTED_STACK_C) + cgettag s1, cra + + csc cra, (-CLEN_BYTES * TRUSTED_FRAME_SIZE + CLEN_BYTES * 1)(TRUSTED_STACK_C) + csc cs0, (-CLEN_BYTES * TRUSTED_FRAME_SIZE + CLEN_BYTES * 0)(TRUSTED_STACK_C) + + /* + * Save the caller's current stack top and old stack top. + */ + csc csp, (-CLEN_BYTES * TRUSTED_FRAME_SIZE + TRUSTED_FRAME_SP_OSP)(TRUSTED_STACK_C) + csc ct5, (-CLEN_BYTES * TRUSTED_FRAME_SIZE + TRUSTED_FRAME_SP_OSP + CLEN_BYTES)(TRUSTED_STACK_C) + /* + * Save the address of the previous trusted frame. + */ + csc TRUSTED_STACK_C, (-CLEN_BYTES * TRUSTED_FRAME_SIZE + TRUSTED_FRAME_PREV)(TRUSTED_STACK_C) + /* + * Save the the compartment ID of the caller. + */ + csw t4, (-CLEN_BYTES * TRUSTED_FRAME_SIZE + TRUSTED_FRAME_CALLER)(TRUSTED_STACK_C) + + /* + * Get the landing address. + */ +.option push +.option norvc +7: addi t0, t0, 0 /* To be patched at runtime */ +.option pop + + /* + * Compare the return address to the landing address. + * + * Bump the trusted stack pointer if the call is not a tail-call. The + * callee-related portion of the trsuted stack must be written after + * this so that the topmost trusted frame contains the correct + * information about the callee regardless of whether the call is a + * tail-call. + */ + bne ra, s3, 8f + bnez s1, 9f +8: + cincoffsetimm TRUSTED_STACK_C, TRUSTED_STACK_C, (-CLEN_BYTES * TRUSTED_FRAME_SIZE) + csc TRUSTED_STACK_C, TRUSTED_STACK(ct1) + addi s2, x0, 0 +9: + + /* + * Save the callee's compartment ID. + */ + csh s9, TRUSTED_FRAME_CALLEE(TRUSTED_STACK_C) + /* + * Compute and save the number of return value registers. + */ +.option push +.option norvc +10: ori t1, s2, 0 /* To be patched at runtime */ +.option pop + csb t1, (TRUSTED_FRAME_CALLEE + 2)(TRUSTED_STACK_C) + /* + * Save the landing address. + */ + csd t0, TRUSTED_FRAME_LANDING(TRUSTED_STACK_C) + + /* + * Install the callee's stack. + * + * Note: If an interrupt occurs between this instruction and the + * previous store instruction, the callee as identified by the topmost + * trusted frame would be inconsistent with the untrusted stack. + */ + set_untrusted_stk cs5 +TRAMPEND(tramp_push_frame) + +PATCH_POINT(tramp_push_frame, pcc, 1b) +PATCH_POINT(tramp_push_frame, unsealer, 2b) +PATCH_POINT(tramp_push_frame, cid, 3b) +PATCH_POINT(tramp_push_frame, target, 5b) +PATCH_POINT(tramp_push_frame, landing, 7b) +PATCH_POINT(tramp_push_frame, n_rets, 10b) + +/* + * Save the address of the current frame to fp so that unwinders can locate it. + * When transitioning to Restricted mode code, its tag must be cleared. + */ +TRAMP(tramp_update_fp) + cmove cs0, TRUSTED_STACK_C +TRAMPEND(tramp_update_fp) + +TRAMP(tramp_update_fp_untagged) + ccleartag cs0, TRUSTED_STACK_C +TRAMPEND(tramp_update_fp_untagged) + +TRAMP(tramp_clear_args_a67) +/* xx */ +1: cclear 2, 0b00000000 /* To be patched at runtime */ +TRAMPEND(tramp_clear_args_a67) + +PATCH_POINT(tramp_clear_args_a67, a67, 1b) + +TRAMP(tramp_clear_args_a05) +/* xxxxxx */ +1: cclear 1, 0b00000000 /* To be patched at runtime */ +TRAMPEND(tramp_clear_args_a05) + +PATCH_POINT(tramp_clear_args_a05, a05, 1b) + +TRAMP(tramp_clear_args) +.option push +.option rvc + /* + * Each instruction here is 2 bytes long. + */ + addi a7, x0, 0 + addi a6, x0, 0 + addi a5, x0, 0 + addi a4, x0, 0 + addi a3, x0, 0 + addi a2, x0, 0 + addi a1, x0, 0 + addi a0, x0, 0 +.option pop +TRAMPEND(tramp_clear_args) + +TRAMP(tramp_invoke) + ccleartag TRUSTED_STACK_C, TRUSTED_STACK_C + cjalr cra, cs4 +TRAMPEND(tramp_invoke) + +TRAMP(tramp_pop_frame) +1: auipcc ct4, 0 + /* + * Get the unsealer for the thread identifier buffer. + */ +.option push +.option norvc +2: clc ct4, 0(ct4) /* To be patched at runtime */ +.option pop + /* + * Get and unseal the thread identifier buffer. + */ + cspecialr ct0, TIDC + cunseal ct4, ct0, ct4 + + /* + * Get the trusted stack. + */ + clc TRUSTED_STACK_C, TRUSTED_STACK(ct4) + /* + * Load the address of the previous trusted frame, the compartment ID of + * the caller, and the compartment ID of the caller. + */ + clc ct5, TRUSTED_FRAME_PREV(TRUSTED_STACK_C) + clhu t1, TRUSTED_FRAME_CALLER(TRUSTED_STACK_C) + clbu t2, (TRUSTED_FRAME_CALLEE + 2)(TRUSTED_STACK_C) + /* + * Load the caller's current stack top and old stack top. + */ + clc ca7, (TRUSTED_FRAME_SP_OSP)(TRUSTED_STACK_C) + clc ca6, (TRUSTED_FRAME_SP_OSP + CLEN_BYTES)(TRUSTED_STACK_C) + + /* + * Get the stack lookup table. + */ + clc STACK_TABLE_C, STACK_TABLE(ct4) + + /* + * Restore callee-saved registers. + */ + clc cs0, (CLEN_BYTES * 0)(TRUSTED_STACK_C) + clc cra, (CLEN_BYTES * 1)(TRUSTED_STACK_C) + clc cs1, (CLEN_BYTES * 2)(TRUSTED_STACK_C) + clc cs2, (CLEN_BYTES * 3)(TRUSTED_STACK_C) + clc cs3, (CLEN_BYTES * 4)(TRUSTED_STACK_C) + clc cs4, (CLEN_BYTES * 5)(TRUSTED_STACK_C) + clc cs5, (CLEN_BYTES * 6)(TRUSTED_STACK_C) + clc cs6, (CLEN_BYTES * 7)(TRUSTED_STACK_C) + clc cs7, (CLEN_BYTES * 8)(TRUSTED_STACK_C) + clc cs8, (CLEN_BYTES * 9)(TRUSTED_STACK_C) + clc cs9, (CLEN_BYTES * 10)(TRUSTED_STACK_C) + clc cs10, (CLEN_BYTES * 11)(TRUSTED_STACK_C) + clc cs11, (CLEN_BYTES * 12)(TRUSTED_STACK_C) + + /* + * Install the caller's stack. + * + * Note: If an interrupt occurs between this instruction and the next, + * the callee as identified by the topmost trusted frame would be + * inconsistent with the untrusted stack. + */ + set_untrusted_stk ca7 + /* + * Bump the trusted stack pointer. + */ + csc ct5, TRUSTED_STACK(ct4) + + /* + * Store the caller's old stack top in the stack lookup table. + */ + csetoffset STACK_TABLE_C, STACK_TABLE_C, t1 + csc ca6, 0(STACK_TABLE_C) + + /* + * Extract the number of return value registers. The number of return + * value registers is encoded as follows: + * - TWO: 0b00 + * - ONE: 0b01 + * - NONE: 0b10 + * - INDIRECT: 0b11 + */ + beqz t2, 3f + addi a1, x0, 1 + beq t2, a1, 3f + addi a0, x0, 0 +3: + + /* + * Clear temporary registers. + * cclear 1, 0b11110000 a2 to a5 + * cclear 3, 0b11110000 t3 to t6 + */ + addi a2, x0, 0 + addi a3, x0, 0 + addi a4, x0, 0 + addi a5, x0, 0 + addi t3, x0, 0 + addi t4, x0, 0 + addi t5, x0, 0 + addi t6, x0, 0 + + cret +TRAMPEND(tramp_pop_frame) + +PATCH_POINT(tramp_pop_frame, pcc, 1b) +PATCH_POINT(tramp_pop_frame, unsealer, 2b) diff --git a/libexec/rtld-elf/riscv/rtld_c18n_machdep.c b/libexec/rtld-elf/riscv/rtld_c18n_machdep.c new file mode 100644 index 000000000000..762ca09e674f --- /dev/null +++ b/libexec/rtld-elf/riscv/rtld_c18n_machdep.c @@ -0,0 +1,156 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2024 Dapeng Gao + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#include +#include + +#include + +#include + +#include "debug.h" +#include "rtld.h" +#include "rtld_c18n.h" +#include "rtld_libc.h" + +/* + * Trampolines + */ +size_t +tramp_compile(char **entry, const struct tramp_data *data) +{ +#define IMPORT(template) \ + extern const uint32_t tramp_##template[]; \ + extern const size_t size_tramp_##template + + IMPORT(push_frame); + IMPORT(update_fp); + IMPORT(update_fp_untagged); + IMPORT(clear_args); + IMPORT(invoke); + IMPORT(pop_frame); + + size_t size = 0; + char *buf = *entry; + size_t sealer_off, target_off, pcc_off, landing_off, unused_regs; + +#define COPY_VALUE(val) ({ \ + size_t _old_size = size; \ + *(typeof(val) *)(buf + size) = val; \ + size += sizeof(val); \ + _old_size; \ +}) + +#define COPY(template) \ + do { \ + memcpy(buf + size, tramp_##template, \ + size_tramp_##template); \ + size += size_tramp_##template; \ + } while(0) + +#define PATCH_INS(offset) ((uint32_t *)(buf + (offset))) \ + +#define PATCH_OFF(tramp, name) ({ \ + extern const int32_t patch_tramp_##tramp##_##name; \ + size + patch_tramp_##tramp##_##name; \ + }) + +#define PATCH_I_TYPE(tramp, name, value) \ + do { \ + uint32_t _value = (value); \ + _value = ((_value & 0xfff) << 20); \ + *PATCH_INS(PATCH_OFF(tramp, name)) |= _value; \ + } while (0) + +#define PATCH_LANDING(landing_off, value) \ + do { \ + uint32_t _value = (value); \ + _value = ((_value & 0xfff) << 20); \ + *PATCH_INS(landing_off) |= _value; \ + } while (0) + +#define PATCH_CClear(tramp, name, value) \ + do { \ + uint32_t _value = (value); \ + _value = ((_value & 0x1f) << 7) | \ + ((_value & 0xe0) << 10); \ + *PATCH_INS(PATCH_OFF(tramp, name)) |= _value; \ + } while (0) + + sealer_off = COPY_VALUE(sealer_tidc); + + *(struct tramp_header *)(buf + size) = (struct tramp_header) { + .target = data->target, + .defobj = data->defobj, + .symnum = data->def == NULL ? + 0 : data->def - data->defobj->symtab, + .sig = data->sig + }; + target_off = size + offsetof(struct tramp_header, target); + *entry = buf + size; + size += offsetof(struct tramp_header, entry); + + COPY(push_frame); + pcc_off = PATCH_OFF(push_frame, pcc); + PATCH_I_TYPE(push_frame, unsealer, sealer_off - pcc_off); + PATCH_I_TYPE(push_frame, cid, + cid_to_index(data->defobj->compart_id).val); + PATCH_I_TYPE(push_frame, target, target_off - pcc_off); + landing_off = PATCH_OFF(push_frame, landing); + /* + * The number of return value registers is encoded as follows: + * - TWO: 0b00 + * - ONE: 0b01 + * - NONE: 0b10 + * - INDIRECT: 0b11 + */ + PATCH_I_TYPE(push_frame, n_rets, + data->sig.valid ? data->sig.ret_args : 0); + + if (ld_compartment_unwind != NULL) + COPY(update_fp); + else + COPY(update_fp_untagged); + + if (data->sig.valid) { + /* Each instruction here is 2 bytes long. */ + unused_regs = (8 - data->sig.reg_args) * + sizeof(*tramp_clear_args) / 2; + memcpy(buf + size, tramp_clear_args, unused_regs); + size += unused_regs; + } + COPY(invoke); + PATCH_LANDING(landing_off, size - pcc_off); + + COPY(pop_frame); + pcc_off = PATCH_OFF(pop_frame, pcc); + PATCH_I_TYPE(pop_frame, unsealer, sealer_off - pcc_off); + + return (size); +} diff --git a/libexec/rtld-elf/riscv/rtld_c18n_machdep.h b/libexec/rtld-elf/riscv/rtld_c18n_machdep.h new file mode 100644 index 000000000000..f5ce245c961d --- /dev/null +++ b/libexec/rtld-elf/riscv/rtld_c18n_machdep.h @@ -0,0 +1,140 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2024 Dapeng Gao + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef RTLD_C18N_MACHDEP_H +#define RTLD_C18N_MACHDEP_H + +#include +#include + +#define TRUSTED_FRAME_SIZE 17 +#define TRUSTED_FRAME_SP_OSP (16 * 13) +#define TRUSTED_FRAME_PREV (16 * 15) +#define TRUSTED_FRAME_CALLER (16 * 16) +#define TRUSTED_FRAME_CALLEE (16 * 16 + 4) +#define TRUSTED_FRAME_LANDING (16 * 16 + 8) + +#define SIG_FRAME_SIZE 1376 + +#define UNTRUSTED_STACK csp +#define TIDC 3 +#define TRUSTED_STACK 0 +#define STACK_TABLE CLEN_BYTES +#define STACK_TABLE_N 6 +#define STACK_TABLE_C __CONCAT(ct, STACK_TABLE_N) +#define STACK_TABLE_RTLD 32 + +#ifdef IN_ASM + +.macro get_untrusted_stk reg + cmove \reg, UNTRUSTED_STACK +.endmacro + +.macro set_untrusted_stk reg + cmove UNTRUSTED_STACK, \reg +.endmacro + +.macro get_rtld_stk reg + clgc \reg, sealer_tidc + clc \reg, 0(\reg) + cspecialr STACK_TABLE_C, TIDC + cunseal STACK_TABLE_C, STACK_TABLE_C, \reg + clc STACK_TABLE_C, STACK_TABLE(STACK_TABLE_C) + clc \reg, STACK_TABLE_RTLD(STACK_TABLE_C) +.endmacro + +#else + +static inline void * +get_trusted_tp(void) +{ + void *ptr; + + asm volatile ("cmove %0, ctp" : "=C" (ptr)); + return (ptr); +} + +struct tidc { + struct trusted_frame *trusted_stk; + struct stk_table *table; +}; + +static inline struct stk_table * +get_stk_table(void) +{ + struct tidc *tidc; + + asm volatile ("cspecialr %0, " __XSTRING(TIDC) : "=C" (tidc)); + tidc = cheri_unseal(tidc, sealer_tidc); + return (tidc->table); +} + +static inline void +set_stk_table(struct stk_table *table) +{ + struct tidc *tidc; + + asm volatile ("cspecialr %0, " __XSTRING(TIDC) : "=C" (tidc)); + tidc = cheri_unseal(tidc, sealer_tidc); + tidc->table = table; +} + +static inline struct trusted_frame * +get_trusted_stk(void) +{ + struct tidc *tidc; + + asm volatile ("cspecialr %0, " __XSTRING(TIDC) : "=C" (tidc)); + tidc = cheri_unseal(tidc, sealer_tidc); + return (tidc->trusted_stk); +} + +static inline void +set_trusted_stk(struct trusted_frame *tf) +{ + struct tidc *tidc; + + asm volatile ("cspecialr %0, " __XSTRING(TIDC) : "=C" (tidc)); + tidc = cheri_unseal(tidc, sealer_tidc); + tidc->trusted_stk = tf; +} + +struct compart_state { + void *fp; /* s0 */ + void *pc; + /* + * s1 to s11 + */ + void *regs[11]; + /* + * INVARIANT: This field contains the top of the caller's stack when the + * caller made the call. + */ + void *sp; +}; +#endif +#endif diff --git a/libexec/rtld-elf/riscv/rtld_machdep.h b/libexec/rtld-elf/riscv/rtld_machdep.h index aefc352efa24..4a403985212c 100644 --- a/libexec/rtld-elf/riscv/rtld_machdep.h +++ b/libexec/rtld-elf/riscv/rtld_machdep.h @@ -154,11 +154,29 @@ make_data_cap(const Elf_Sym *def, const struct Struct_Obj_Entry *defobj) #define call_init_pointer(obj, target) rtld_fatal("%s: _init or _fini used!", obj->path) /* TODO: Per-function captable/PLT/FNDESC support (needs CGP) */ +#ifdef CHERI_LIB_C18N +#define call_init_array_pointer(_obj, _target) \ + (((InitArrFunc)tramp_intern(NULL, &(struct tramp_data) { \ + .target = (void *)(_target).value, \ + .defobj = _obj, \ + .sig = (struct func_sig) { .valid = true, \ + .reg_args = 3, .mem_args = false, .ret_args = NONE }\ + }))(main_argc, main_argv, environ)) + +#define call_fini_array_pointer(_obj, _target) \ + (((InitFunc)tramp_intern(NULL, &(struct tramp_data) { \ + .target = (void *)(_target).value, \ + .defobj = _obj, \ + .sig = (struct func_sig) { .valid = true, \ + .reg_args = 0, .mem_args = false, .ret_args = NONE }\ + }))()) +#else #define call_init_array_pointer(obj, target) \ (((InitArrFunc)(target).value)(main_argc, main_argv, environ)) #define call_fini_array_pointer(obj, target) \ (((InitFunc)(target).value)()) +#endif #else /* __CHERI_PURE_CAPABILITY__ */ diff --git a/libexec/rtld-elf/riscv/rtld_start.S b/libexec/rtld-elf/riscv/rtld_start.S index e220a158f695..5a2c03750997 100644 --- a/libexec/rtld-elf/riscv/rtld_start.S +++ b/libexec/rtld-elf/riscv/rtld_start.S @@ -55,6 +55,8 @@ * _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) */ +#ifndef C18N_VARIANT + ENTRY(.rtld_start) #ifdef __CHERI_PURE_CAPABILITY__ cmove cs0, ca0 /* Put aux in a callee-saved register */ @@ -226,3 +228,5 @@ ENTRY(_rtld_bind_start) JR_PTR PTR(t0) #endif /* defined(__CHERI_PURE_CAPABILITY__) */ END(_rtld_bind_start) + +#endif /* C18N_VARIANT */ diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c index bf7c191f6482..01d0927fefc5 100644 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -873,7 +873,9 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) ld_elf_hints_default = _PATH_ELF_HINTS_C18N; ld_path_libmap_conf = _PATH_LIBMAP_CONF_C18N; ld_standard_library_path = STANDARD_LIBRARY_PATH_C18N; +#ifdef HAS_RESTRICTED_MODE c18n_code_perm_clear = CHERI_PERM_EXECUTIVE; +#endif } #endif @@ -921,7 +923,7 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) assert(aux_info[AT_PHENT]->a_un.a_val == sizeof(Elf_Phdr)); assert(aux_info[AT_ENTRY] != NULL); imgentry = (dlfunc_t) aux_info[AT_ENTRY]->a_un.a_ptr; -#ifdef CHERI_LIB_C18N +#ifdef HAS_RESTRICTED_MODE imgentry = (dlfunc_t) cheri_clearperm(imgentry, c18n_code_perm_clear); #endif dbg("Values from kernel:\n\tAT_PHDR=" PTR_FMT "\n" diff --git a/libexec/rtld-elf/rtld_c18n.c b/libexec/rtld-elf/rtld_c18n.c index 82f1e48018ff..c92ca6310741 100644 --- a/libexec/rtld-elf/rtld_c18n.c +++ b/libexec/rtld-elf/rtld_c18n.c @@ -69,6 +69,9 @@ #include #include +#ifdef CHERI_LIB_C18N +#include +#endif #include @@ -110,7 +113,7 @@ _Static_assert(sizeof(struct func_sig) == sizeof(func_sig_int), "Unexpected func_sig size"); _Static_assert( - SIG_FRAME_SIZE == sizeof(struct sigframe), + SIG_FRAME_SIZE <= sizeof(struct sigframe), "Unexpected struct sigframe size"); /* @@ -121,11 +124,15 @@ static uintptr_t sealer_trusted_stk; uintptr_t sealer_pltgot, sealer_tramp; -/* Enable compartmentalisation */ -bool ld_compartment_enable; - +#ifdef HAS_RESTRICTED_MODE /* Permission bit to be cleared for user code */ uint64_t c18n_code_perm_clear; +#else +uintptr_t sealer_tidc; +#endif + +/* Enable compartmentalisation */ +bool ld_compartment_enable; /* Use utrace() to log compartmentalisation-related events */ const char *ld_compartment_utrace; @@ -597,6 +604,9 @@ struct tcb_wrapper { struct tcb header __attribute__((cheri_no_subobject_bounds)); struct tcb *tcb; struct stk_table *table; +#ifndef HAS_RESTRICTED_MODE + struct tidc tidc; +#endif }; static void @@ -723,6 +733,9 @@ init_stk_table(struct stk_table *table, struct tcb_wrapper *wrap) char *sp; size_t size; struct trusted_frame *tf; +#ifndef HAS_RESTRICTED_MODE + struct tidc *tidc; +#endif /* * Save the fake tcb in the stack lookup table. @@ -746,7 +759,7 @@ init_stk_table(struct stk_table *table, struct tcb_wrapper *wrap) /* * Record RTLD's stack in the stack lookup table. */ -#ifdef __ARM_MORELLO_PURECAP_BENCHMARK_ABI +#ifndef USE_RESTRICTED_MODE sp = cheri_setoffset(cheri_getstack(), 0); #else /* @@ -771,6 +784,19 @@ init_stk_table(struct stk_table *table, struct tcb_wrapper *wrap) }; table->entries[RTLD_COMPART_ID].stack = sp + size; +#ifndef HAS_RESTRICTED_MODE + /* + * Install the tidc buffer. + */ + tidc = cheri_setboundsexact(&wrap->tidc, sizeof(wrap->tidc)); + tidc = cheri_seal(tidc, sealer_tidc); + if (sysarch(RISCV_SET_UTIDC, &tidc) != 0) { + rtld_fdprintf(STDERR_FILENO, "c18n: Cannot set tidc %#p\n", + tidc); + abort(); + } +#endif + /* * Push a dummy trusted frame indicating that the 'root' compartment is * RTLD. @@ -1243,6 +1269,7 @@ tramp_pgs_append(const struct tramp_data *data) memcpy(tramp, buf, len); header = (struct tramp_header *)(tramp + (bufp - buf)); +#ifdef __aarch64__ /* * Ensure i- and d-cache coherency after writing executable code. The * __clear_cache procedure rounds the addresses to cache-line-aligned @@ -1250,6 +1277,9 @@ tramp_pgs_append(const struct tramp_data *data) * sufficiently large bounds to contain these rounded addresses. */ __clear_cache(cheri_copyaddress(pg, header->entry), tramp + len); +#elif __riscv + asm volatile ("fence.i"); +#endif return (header); } @@ -1299,9 +1329,7 @@ tramp_make_entry(const struct tramp_header *header) const void *entry = header->entry; entry = cheri_clearperm(entry, FUNC_PTR_REMOVE_PERMS); -#ifndef __ARM_MORELLO_PURECAP_BENCHMARK_ABI entry = cheri_capmode(entry); -#endif return (cheri_sealentry(entry)); } @@ -1477,11 +1505,14 @@ tramp_reflect(const void *data) if (!cheri_gettag(data) || !cheri_getsealed(data) || cheri_gettype(data) != CHERI_OTYPE_SENTRY || (cheri_getperm(data) & CHERI_PERM_LOAD) == 0 || - (cheri_getperm(data) & CHERI_PERM_EXECUTE) == 0 || - (cheri_getperm(data) & CHERI_PERM_EXECUTIVE) == 0) + (cheri_getperm(data) & CHERI_PERM_EXECUTE) == 0 +#ifdef HAS_EXECUTIVE_MODE + || (cheri_getperm(data) & CHERI_PERM_EXECUTIVE) == 0 +#endif + ) return (NULL); -#ifndef __ARM_MORELLO_PURECAP_BENCHMARK_ABI +#ifdef USE_RESTRICTED_MODE data = (const char *)data - 1; #endif data = __containerof(data, struct tramp_header, entry); @@ -1596,6 +1627,7 @@ void c18n_init2(void) { uintptr_t sealer; + struct tcb_wrapper *wrap; /* * Allocate otypes for RTLD use @@ -1613,6 +1645,11 @@ c18n_init2(void) sealer_trusted_stk = cheri_setboundsexact(sealer, 1); sealer += 1; +#ifndef HAS_RESTRICTED_MODE + sealer_tidc = cheri_setboundsexact(sealer, 1); + sealer += 1; +#endif + sealer_tramp = cheri_setboundsexact(sealer, C18N_FUNC_SIG_COUNT); sealer += C18N_FUNC_SIG_COUNT; @@ -1620,7 +1657,9 @@ c18n_init2(void) * All libraries have been loaded. Create and initialise a stack lookup * table with the same size as the number of compartments. */ - init_stk_table(expand_stk_table(NULL, comparts.size), NULL); + wrap = c18n_malloc(sizeof(*wrap)); + *wrap = (struct tcb_wrapper) {}; + init_stk_table(expand_stk_table(NULL, comparts.size), wrap); /* * Create a trampoline table with 2^9 = 512 entries. @@ -1653,7 +1692,9 @@ void _rtld_thr_exit(long *); void _rtld_thread_start_init(void (*p)(struct pthread *)) { +#ifdef HAS_EXECUTIVE_MODE assert((cheri_getperm(p) & CHERI_PERM_EXECUTIVE) == 0); +#endif assert(thr_thread_start == NULL); thr_thread_start = tramp_intern(NULL, &(struct tramp_data) { .target = p, @@ -1702,6 +1743,16 @@ _rtld_thr_exit(long *state) set_stk_table(NULL); set_trusted_stk(NULL); +#ifndef HAS_RESTRICTED_MODE + /* + * Uninstall the tidc buffer. + */ + if (sysarch(RISCV_SET_UTIDC, &(void *) { NULL }) != 0) { + rtld_fdprintf(STDERR_FILENO, "c18n: Cannot clear tidc\n"); + abort(); + } +#endif + /* * Clear RTLD's stack lookup table entry. */ @@ -1789,7 +1840,9 @@ static __siginfohandler_t *signal_dispatcher = sigdispatch; void _rtld_sighandler_init(__siginfohandler_t *handler) { +#ifdef HAS_EXECUTIVE_MODE assert((cheri_getperm(handler) & CHERI_PERM_EXECUTIVE) == 0); +#endif assert(signal_dispatcher == sigdispatch); signal_dispatcher = tramp_intern(NULL, &(struct tramp_data) { .target = handler, @@ -1801,7 +1854,7 @@ _rtld_sighandler_init(__siginfohandler_t *handler) }); } -#ifdef __ARM_MORELLO_PURECAP_BENCHMARK_ABI +#ifndef USE_RESTRICTED_MODE void _rtld_sighandler_impl(int, siginfo_t *, ucontext_t *, void *, struct sigframe *); @@ -1826,7 +1879,7 @@ _rtld_sighandler_impl(int sig, siginfo_t *info, ucontext_t *ucp, void *nsp) table = get_stk_table(); -#ifdef __ARM_MORELLO_PURECAP_BENCHMARK_ABI +#ifndef USE_RESTRICTED_MODE /* * Move the sigframe to RTLD's stack. */ @@ -1895,7 +1948,7 @@ _rtld_sighandler_impl(int sig, siginfo_t *info, ucontext_t *ucp, void *nsp) intr = RTLD_COMPART_ID; if (cheri_is_subset(table->meta->compart_stk[intr].begin, nsp)) goto found_trusted; -#ifndef __ARM_MORELLO_PURECAP_BENCHMARK_ABI +#ifdef USE_RESTRICTED_MODE found_failed: #endif rtld_fdprintf(STDERR_FILENO, @@ -1903,7 +1956,7 @@ _rtld_sighandler_impl(int sig, siginfo_t *info, ucontext_t *ucp, void *nsp) "Please file a bug report!\n", nsp); abort(); found_trusted: -#ifndef __ARM_MORELLO_PURECAP_BENCHMARK_ABI +#ifdef USE_RESTRICTED_MODE /* * The untrusted stack can only become temporarily inconsistent when * running code in Executive mode. This performs a quick sanity check. @@ -1951,7 +2004,11 @@ _rtld_sighandler_impl(int sig, siginfo_t *info, ucontext_t *ucp, void *nsp) * If the interrupted code has loaded the stack lookup table, it would * be located in register STACK_TABLE_N. Check if this is the case. */ +#ifdef __aarch64__ table_reg = &ucp->uc_mcontext.mc_capregs.cap_x[STACK_TABLE_N]; +#elif defined(__riscv) + table_reg = &ucp->uc_mcontext.mc_capregs.cp_ct[STACK_TABLE_N]; +#endif if (!cheri_equal_exact(table, *table_reg)) table_reg = NULL; @@ -1989,7 +2046,7 @@ _rtld_sighandler_impl(int sig, siginfo_t *info, ucontext_t *ucp, void *nsp) * stack and let the kernel install the stack of the interrupted * compartment. */ -#ifndef __ARM_MORELLO_PURECAP_BENCHMARK_ABI +#ifdef USE_RESTRICTED_MODE set_untrusted_stk(ntf->state.sp); #endif } diff --git a/libexec/rtld-elf/rtld_c18n.h b/libexec/rtld-elf/rtld_c18n.h index 9fa00d99d02e..86a581019e79 100644 --- a/libexec/rtld-elf/rtld_c18n.h +++ b/libexec/rtld-elf/rtld_c18n.h @@ -30,11 +30,22 @@ #include +#ifdef __aarch64__ +#define HAS_RESTRICTED_MODE +#ifndef __ARM_MORELLO_PURECAP_BENCHMARK_ABI +#define USE_RESTRICTED_MODE +#endif +#endif + /* * Global symbols */ -extern size_t c18n_code_perm_clear; extern uintptr_t sealer_pltgot, sealer_tramp; +#ifdef HAS_RESTRICTED_MODE +extern size_t c18n_code_perm_clear; +#else +extern uintptr_t sealer_tidc; +#endif extern const char *ld_compartment_utrace; extern const char *ld_compartment_policy; extern const char *ld_compartment_overhead; diff --git a/libexec/rtld-elf/rtld_c18n_mi.S b/libexec/rtld-elf/rtld_c18n_mi.S index 3027ce5c97d1..8f0abe055b9d 100644 --- a/libexec/rtld-elf/rtld_c18n_mi.S +++ b/libexec/rtld-elf/rtld_c18n_mi.S @@ -38,7 +38,7 @@ c18n_default_policy_end: .size c18n_default_policy, . - c18n_default_policy .globl c18n_default_policy_size -.type c18n_default_policy_size,#object +.type c18n_default_policy_size,%object .align 3 c18n_default_policy_size: .quad c18n_default_policy_end - c18n_default_policy From ad36d346d1c6b0b5ed9966f20a3b1b029d3b2af1 Mon Sep 17 00:00:00 2001 From: Dapeng Gao Date: Wed, 17 Jul 2024 22:51:01 +0100 Subject: [PATCH 09/12] riscv: Add option for enabling ubiquitous c18n --- sys/cheri/cheri.h | 2 -- sys/cheri/cheri_sysctl.c | 2 -- sys/kern/imgact_elf.c | 2 +- sys/sys/procctl.h | 2 +- 4 files changed, 2 insertions(+), 6 deletions(-) diff --git a/sys/cheri/cheri.h b/sys/cheri/cheri.h index f4f783b2e168..69717f390766 100644 --- a/sys/cheri/cheri.h +++ b/sys/cheri/cheri.h @@ -168,9 +168,7 @@ extern u_int security_cheri_debugger_on_sandbox_syscall; extern u_int security_cheri_syscall_violations; extern u_int security_cheri_bound_legacy_capabilities; extern u_int cheri_cloadtags_stride; -#ifdef __aarch64__ extern bool security_cheri_lib_based_c18n_default; -#endif #ifdef __CHERI_PURE_CAPABILITY__ /* diff --git a/sys/cheri/cheri_sysctl.c b/sys/cheri/cheri_sysctl.c index 66dd6df95b6a..f72bee34af29 100644 --- a/sys/cheri/cheri_sysctl.c +++ b/sys/cheri/cheri_sysctl.c @@ -80,7 +80,6 @@ SYSCTL_INT(_security_cheri, OID_AUTO, bound_legacy_capabilities, CTLFLAG_RWTUN, &security_cheri_bound_legacy_capabilities, 0, "Set bounds on userspace capabilities created by legacy ABIs."); -#ifdef __aarch64__ /* * Set the default state of library-based compartmentalisation (c18n) in * userspace. @@ -89,7 +88,6 @@ bool security_cheri_lib_based_c18n_default = false; SYSCTL_BOOL(_security_cheri, OID_AUTO, lib_based_c18n_default, CTLFLAG_RWTUN, &security_cheri_lib_based_c18n_default, 0, "Userspace library-based compartmentalisation default"); -#endif #ifdef CHERI_CAPREVOKE /* diff --git a/sys/kern/imgact_elf.c b/sys/kern/imgact_elf.c index 161660a3e956..f1c3b2afbae0 100644 --- a/sys/kern/imgact_elf.c +++ b/sys/kern/imgact_elf.c @@ -1839,7 +1839,7 @@ __elfN(freebsd_copyout_auxargs)(struct image_params *imgp, uintcap_t base) oc = atomic_load_int(&vm_overcommit); bsdflags |= (oc & (SWAP_RESERVE_FORCE_ON | SWAP_RESERVE_RLIMIT_ON)) != 0 ? ELF_BSDF_VMNOOVERCOMMIT : 0; -#if defined(__ELF_CHERI) && defined(__aarch64__) +#ifdef __ELF_CHERI /* * ELF_BSDF_CHERI_C18N tells the runtime linker to enable library-based * compartmentalisation. diff --git a/sys/sys/procctl.h b/sys/sys/procctl.h index f3bdb15fa6bd..d3813b7884eb 100644 --- a/sys/sys/procctl.h +++ b/sys/sys/procctl.h @@ -67,7 +67,7 @@ #define PROC_WXMAP_STATUS 22 /* query W^X */ #define PROC_CHERI_REVOKE_CTL 1001 /* en/dis CHERI revocation */ #define PROC_CHERI_REVOKE_STATUS 1002 /* query CHERI revocation status */ -#if __has_feature(capabilities) && defined(__aarch64__) +#if __has_feature(capabilities) #define PROC_CHERI_C18N_CTL 1003 /* en/dis CHERI compartmentalisation */ #define PROC_CHERI_C18N_STATUS 1004 /* query CHERI compartmentalisation status */ #endif From dee9a30decfe97abd70b80e0538c88a7659d39d6 Mon Sep 17 00:00:00 2001 From: Dapeng Gao Date: Wed, 17 Jul 2024 22:51:14 +0100 Subject: [PATCH 10/12] cheribsdtest: Add c18n tests for CHERI-RISC-V --- bin/cheribsdtest/Makefile | 2 +- bin/cheribsdtest/cheribsdtest_registers.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/cheribsdtest/Makefile b/bin/cheribsdtest/Makefile index 2fe4c81e45aa..8a17ec85ea74 100644 --- a/bin/cheribsdtest/Makefile +++ b/bin/cheribsdtest/Makefile @@ -13,7 +13,7 @@ SUBDIR+= purecap \ purecap-dynamic-mt \ purecap-mt -.if ${MACHINE_CPUARCH} == "aarch64" && ${MACHINE_ABI:Mpurecap} +.if ${MACHINE_ABI:Mpurecap} SUBDIR+= mt-c18n .endif .endif diff --git a/bin/cheribsdtest/cheribsdtest_registers.c b/bin/cheribsdtest/cheribsdtest_registers.c index 444b04fd346c..9e319c384f28 100644 --- a/bin/cheribsdtest/cheribsdtest_registers.c +++ b/bin/cheribsdtest/cheribsdtest_registers.c @@ -172,7 +172,7 @@ check_initreg_code(void * __capability c) (CHERI_PERMS_SWALL & ~CHERI_PERM_SW_VMEM)); /* Check that the raw permission bits match the kernel header: */ -#if defined(CHERIBSD_C18N_TESTS) && !defined(__ARM_MORELLO_PURECAP_BENCHMARK_ABI) +#if defined(CHERIBSD_C18N_TESTS) && defined(__aarch64__) && !defined(__ARM_MORELLO_PURECAP_BENCHMARK_ABI) if (v != (CHERI_CAP_USER_CODE_PERMS & ~CHERI_PERM_EXECUTIVE)) cheribsdtest_failure_errx("perms %jx (expected %jx)", v, (uintmax_t)(CHERI_CAP_USER_CODE_PERMS & ~CHERI_PERM_EXECUTIVE)); From 247fed68c6396da3eba042043869f1c87e73ea85 Mon Sep 17 00:00:00 2001 From: Dapeng Gao Date: Fri, 19 Jul 2024 14:28:00 +0100 Subject: [PATCH 11/12] c18n: Port {libc,libthr}_c18n to RISC-V --- lib/Makefile | 2 +- lib/libc/riscv/gen/_setjmp.S | 27 ++++++++++++++++++++++++++ lib/libc/riscv/gen/setjmp.S | 27 ++++++++++++++++++++++++++ lib/libsys/riscv/Makefile.sys | 7 +++++-- libexec/rtld-elf/riscv/rtld_c18n_asm.S | 3 --- libexec/rtld-elf/rtld.c | 14 +++++++++++++ libexec/rtld-elf/rtld_c18n.c | 9 ++++++++- 7 files changed, 82 insertions(+), 7 deletions(-) diff --git a/lib/Makefile b/lib/Makefile index f1121c8f829d..405522cb7f70 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -113,7 +113,7 @@ SUBDIR= ${SUBDIR_BOOTSTRAP} \ ncurses \ nss_tacplus -.if ${MACHINE_ABI:Mpurecap} && ${MACHINE_CPUARCH} == "aarch64" +.if ${MACHINE_ABI:Mpurecap} && (${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "riscv") .if !defined(COMPAT_LIBCOMPAT) || ${COMPAT_LIBCOMPAT} == "64CB" SUBDIR+= c18n .endif diff --git a/lib/libc/riscv/gen/_setjmp.S b/lib/libc/riscv/gen/_setjmp.S index 050113cb0e26..0d22ae268167 100644 --- a/lib/libc/riscv/gen/_setjmp.S +++ b/lib/libc/riscv/gen/_setjmp.S @@ -80,8 +80,19 @@ ENTRY(_setjmp) #endif /* Return value */ +#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) + cmove ca1, ca0 +#endif li a0, 0 +#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) + /* + * Tail-call to save trusted stack state + */ + clgc ct0, _C_LABEL(c18n_get_trusted_stk) + cjr ct0 +#else cret +#endif #else /* Store the magic value and stack pointer */ ld t0, .Lmagic @@ -140,7 +151,11 @@ ENTRY(_longjmp) bne t0, t1, botch /* Restore the stack pointer */ +#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) + clc ca3, 16(ca0) +#else clc csp, 16(ca0) +#endif cincoffset ca0, ca0, (2 * 16) /* Restore the general purpose registers and ra */ @@ -179,8 +194,20 @@ ENTRY(_longjmp) #endif /* Load the return value */ +#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) + clc ca2, 0(ca0) +#endif mv a0, a1 +#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) + cmove ca1, ca3 + /* + * Tail-call to restore trusted stack state + */ + clgc ct0, _C_LABEL(c18n_unwind_trusted_stk) + cjr ct0 +#else cret +#endif #else /* Check the magic value */ ld t0, 0(a0) diff --git a/lib/libc/riscv/gen/setjmp.S b/lib/libc/riscv/gen/setjmp.S index 672f7f50482b..3a9d7035ab93 100644 --- a/lib/libc/riscv/gen/setjmp.S +++ b/lib/libc/riscv/gen/setjmp.S @@ -95,8 +95,19 @@ ENTRY(setjmp) #endif /* Return value */ +#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) + cmove ca1, ca0 +#endif li a0, 0 +#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) + /* + * Tail-call to save trusted stack state + */ + clgc ct0, _C_LABEL(c18n_get_trusted_stk) + cjr ct0 +#else cret +#endif #else addi sp, sp, -(2 * 8) sd a0, 0(sp) @@ -186,7 +197,11 @@ ENTRY(longjmp) cincoffset csp, csp, (3 * 16) /* Restore the stack pointer */ +#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) + clc ca3, 16(ca0) +#else clc csp, 16(ca0) +#endif cincoffset ca0, ca0, (2 * 16) /* Restore the general purpose registers and ra */ @@ -225,8 +240,20 @@ ENTRY(longjmp) #endif /* Load the return value */ +#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) + clc ca2, 0(ca0) +#endif mv a0, a1 +#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) + cmove ca1, ca3 + /* + * Tail-call to restore trusted stack state + */ + clgc ct0, _C_LABEL(c18n_unwind_trusted_stk) + cjr ct0 +#else cret +#endif botch: clgc ct0, _C_LABEL(longjmperror) diff --git a/lib/libsys/riscv/Makefile.sys b/lib/libsys/riscv/Makefile.sys index 2ff84735f484..5cfd48643944 100644 --- a/lib/libsys/riscv/Makefile.sys +++ b/lib/libsys/riscv/Makefile.sys @@ -1,5 +1,8 @@ SRCS+= __vdso_gettc.c \ sched_getcpu_gen.c -MDASM= cerror.S \ - vfork.S +MDASM= cerror.S + +.ifndef CHERI_LIB_C18N +MDASM+= vfork.S +.endif diff --git a/libexec/rtld-elf/riscv/rtld_c18n_asm.S b/libexec/rtld-elf/riscv/rtld_c18n_asm.S index f2834fa58bd2..55bc6ea25d10 100644 --- a/libexec/rtld-elf/riscv/rtld_c18n_asm.S +++ b/libexec/rtld-elf/riscv/rtld_c18n_asm.S @@ -409,8 +409,6 @@ TRAMPEND(tramp_clear_args_a05) PATCH_POINT(tramp_clear_args_a05, a05, 1b) TRAMP(tramp_clear_args) -.option push -.option rvc /* * Each instruction here is 2 bytes long. */ @@ -422,7 +420,6 @@ TRAMP(tramp_clear_args) addi a2, x0, 0 addi a1, x0, 0 addi a0, x0, 0 -.option pop TRAMPEND(tramp_clear_args) TRAMP(tramp_invoke) diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c index 01d0927fefc5..2920b46e0879 100644 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -4499,9 +4499,23 @@ do_dlsym(void *handle, const char *name, void *retaddr, const Ver_Entry *ve, if (ELF_ST_TYPE(def->st_info) == STT_FUNC) { sym = __DECONST(void*, make_function_pointer(def, defobj)); dbg("dlsym(%s) is function: " PTR_FMT, name, sym); +#if defined(CHERI_LIB_C18N) && defined(__riscv) + sym = tramp_intern(NULL, &(struct tramp_data) { + .target = sym, + .defobj = defobj, + .def = def + }); +#endif } else if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) { sym = rtld_resolve_ifunc(defobj, def); dbg("dlsym(%s) is ifunc. Resolved to: " PTR_FMT, name, sym); +#if defined(CHERI_LIB_C18N) && defined(__riscv) + sym = tramp_intern(NULL, &(struct tramp_data) { + .target = sym, + .defobj = defobj, + .def = def + }); +#endif } else if (ELF_ST_TYPE(def->st_info) == STT_TLS) { ti.ti_module = defobj->tlsindex; ti.ti_offset = def->st_value; diff --git a/libexec/rtld-elf/rtld_c18n.c b/libexec/rtld-elf/rtld_c18n.c index c92ca6310741..a5ef06ba4af7 100644 --- a/libexec/rtld-elf/rtld_c18n.c +++ b/libexec/rtld-elf/rtld_c18n.c @@ -1718,7 +1718,14 @@ _rtld_thread_start(struct pthread *curthread) * new thread. Extract and install the actual tcb and stack lookup * table. */ - wrap = __containerof(get_trusted_tp(), struct tcb_wrapper, header); + tcb = get_trusted_tp(); +#ifdef __riscv + /* + * The TCB is shifted by the kernel on RISC-V. See `cpu_set_user_tls`. + */ + tcb -= 1; +#endif + wrap = __containerof(tcb, struct tcb_wrapper, header); tcb = cheri_unseal(wrap->tcb, sealer_tcb); *tcb = wrap->header; From cbc5a66c363c72a833f7cfd002da10068fbed7b3 Mon Sep 17 00:00:00 2001 From: Dapeng Gao Date: Tue, 20 Aug 2024 13:49:11 +0100 Subject: [PATCH 12/12] c18n: Add CHERI-RISC-V workaround for lack of memarg ABI --- libexec/rtld-elf/rtld_c18n.c | 17 +++++++++++ libexec/rtld-elf/rtld_c18n_policy.txt | 43 ++++++++------------------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/libexec/rtld-elf/rtld_c18n.c b/libexec/rtld-elf/rtld_c18n.c index a5ef06ba4af7..0e744e99bc9e 100644 --- a/libexec/rtld-elf/rtld_c18n.c +++ b/libexec/rtld-elf/rtld_c18n.c @@ -343,6 +343,17 @@ compart_id_allocate(const char *lib) if (string_base_search(&comparts.data[i].libs, lib) != -1) return (i); +#ifdef __riscv + /* + * XXX: All compartments have the same compartment ID on CHERI-RISC-V to + * work around the lack of bounded memory and variadic arguments. + */ + lib = "[grand]"; + for (i = RTLD_COMPART_ID; i < comparts.size; ++i) + if (string_base_search(&comparts.data[i].libs, lib) != -1) + return (i); +#endif + com = add_comparts_data(lib); string_base_push(&com->libs, lib); @@ -571,8 +582,14 @@ tramp_should_include(const Obj_Entry *reqobj, const struct tramp_data *data) if (reqobj == NULL) return (true); +#ifndef __riscv + /* + * XXX: All compartments have the same compartment ID on CHERI-RISC-V to + * work around the lack of bounded memory and variadic arguments. + */ if (reqobj->compart_id == data->defobj->compart_id) return (false); +#endif if (string_base_search(&comparts.data[reqobj->compart_id].trusts, sym) != -1) diff --git a/libexec/rtld-elf/rtld_c18n_policy.txt b/libexec/rtld-elf/rtld_c18n_policy.txt index a3840600ddd3..401637b62619 100644 --- a/libexec/rtld-elf/rtld_c18n_policy.txt +++ b/libexec/rtld-elf/rtld_c18n_policy.txt @@ -1,13 +1,5 @@ Version 1 -compartment [TCB] - libc.so.7 - libthr.so.3 - libsys.so.7 - -compartment [libunwind] - libgcc_s.so.1 - caller * trust memset @@ -44,27 +36,18 @@ trust _setjmp sigsetjmp unw_getcontext + __sys_vfork + __sys_rfork + _execve + execve + fxecve + execl + execlp + execle + exect + execv + execvp + execvpe + execvP _rtld_thread_start _rtld_sighandler - -callee [RTLD] -export to [TCB] - _rtld_thread_start_init - _rtld_thread_start - _rtld_thr_exit - _rtld_sighandler_init - _rtld_sighandler - _rtld_siginvoke - _rtld_sigaction - -callee [RTLD] -export to [TCB] -export to [libunwind] - c18n_get_trusted_stk - c18n_unwind_trusted_stk - -callee [RTLD] -export to [libunwind] - c18n_is_enabled - c18n_is_tramp - c18n_pop_trusted_stk