diff --git a/bin/cheribsdtest/cheribsdtest.h b/bin/cheribsdtest/cheribsdtest.h index 396e2fae1621..a071fd67197f 100644 --- a/bin/cheribsdtest/cheribsdtest.h +++ b/bin/cheribsdtest/cheribsdtest.h @@ -151,15 +151,6 @@ extern struct cheribsdtest_child_state *ccsp; #endif #endif -#ifndef XFAIL_C18N_FPTR_CANON -#ifdef CHERIBSD_C18N_TESTS -#define XFAIL_C18N_FPTR_CANON \ - "function pointers are currently non-canonical with library-based compartmentalisation" -#else -#define XFAIL_C18N_FPTR_CANON NULL -#endif -#endif - struct cheri_test { const char *ct_name; const char *ct_desc; diff --git a/bin/cheribsdtest/cheribsdtest_fptr_canon.c b/bin/cheribsdtest/cheribsdtest_fptr_canon.c index 998610c08938..164253f1dd78 100644 --- a/bin/cheribsdtest/cheribsdtest_fptr_canon.c +++ b/bin/cheribsdtest/cheribsdtest_fptr_canon.c @@ -37,8 +37,7 @@ #include "cheribsdtest.h" CHERIBSDTEST(fptr_canon_cross, - "Check that function pointers are canonical across objects", - .ct_xfail_reason = XFAIL_C18N_FPTR_CANON) + "Check that function pointers are canonical across objects") { void (* volatile fptr_inside)(void); void (* volatile fptr_outside)(void); @@ -53,8 +52,7 @@ CHERIBSDTEST(fptr_canon_cross, } CHERIBSDTEST(fptr_canon_dlsym, - "Check that function pointers are canonical for dlsym", - .ct_xfail_reason = XFAIL_C18N_FPTR_CANON) + "Check that function pointers are canonical for dlsym") { void (* volatile fptr_inside)(void); void (* volatile fptr_dlsym)(void); @@ -70,8 +68,7 @@ CHERIBSDTEST(fptr_canon_dlsym, } CHERIBSDTEST(fptr_canon_dlfunc, - "Check that function pointers are canonical for dlfunc", - .ct_xfail_reason = XFAIL_C18N_FPTR_CANON) + "Check that function pointers are canonical for dlfunc") { void (* volatile fptr_inside)(void); void (* volatile fptr_dlfunc)(void); diff --git a/lib/libc/sys/sigaction.c b/lib/libc/sys/sigaction.c index bc8f938bd932..7cc21e9421a6 100644 --- a/lib/libc/sys/sigaction.c +++ b/lib/libc/sys/sigaction.c @@ -39,8 +39,10 @@ __weak_reference(sigaction, __libc_sigaction); /* * These weak symbols will always be resolved at runtime. */ -#pragma weak _rtld_sighandler -void _rtld_sighandler(int, siginfo_t *, void *); +/* + * XXX: Explicit function pointer used so that RTLD can wrap it in trampoline. + */ +extern void (*_rtld_sighandler)(int, siginfo_t *, void *); #pragma weak _rtld_sigaction_begin void *_rtld_sigaction_begin(int, struct sigaction *); diff --git a/lib/libthr/thread/thr_sig.c b/lib/libthr/thread/thr_sig.c index fde00d08eacb..dc3fb0b10c3c 100644 --- a/lib/libthr/thread/thr_sig.c +++ b/lib/libthr/thread/thr_sig.c @@ -76,11 +76,15 @@ static void check_cancel(struct pthread *curthread, ucontext_t *ucp); /* * These weak symbols will always be resolved at runtime. */ -#pragma weak _rtld_sighandler -void _rtld_sighandler(int, siginfo_t *, void *); +/* + * XXX: Explicit function pointer used so that RTLD can wrap it in trampoline. + */ +extern void (*_rtld_sighandler)(int, siginfo_t *, void *); -#pragma weak _rtld_dispatch_signal -void _rtld_dispatch_signal(int, siginfo_t *, void *); +/* + * XXX: Explicit function pointer used so that RTLD can wrap it in trampoline. + */ +extern void (*_rtld_dispatch_signal)(int, siginfo_t *, void *); #pragma weak _rtld_sigaction_begin void *_rtld_sigaction_begin(int, struct sigaction *); diff --git a/libexec/rtld-elf/aarch64/rtld_c18n_asm.S b/libexec/rtld-elf/aarch64/rtld_c18n_asm.S index 1395124ac588..8d81fd863574 100644 --- a/libexec/rtld-elf/aarch64/rtld_c18n_asm.S +++ b/libexec/rtld-elf/aarch64/rtld_c18n_asm.S @@ -80,7 +80,7 @@ END(_rtld_thread_start) * is only ever invoked via a trampoline by the kernel when a signal is * delivered. */ -ENTRY(_rtld_sighandler) +ENTRY(_rtld_sighandler_unsafe) #ifndef __ARM_MORELLO_PURECAP_BENCHMARK_ABI /* * The sigframe is pushed onto the trusted stack, disrupting the linked- @@ -116,7 +116,7 @@ ENTRY(_rtld_sighandler) mov c30, c19 b sighandler_unfix_link #endif -END(_rtld_sighandler) +END(_rtld_sighandler_unsafe) /* * void _rtld_dispatch_signal(int, siginfo_t *, void *); @@ -125,7 +125,7 @@ END(_rtld_sighandler) * is only ever invoked by either RTLD code that is aware of this behaviour or * external code via a trampoline. */ -ENTRY(_rtld_dispatch_signal) +ENTRY(_rtld_dispatch_signal_unsafe) mov c24, c30 mov w25, w0 mov c26, c1 @@ -151,7 +151,7 @@ ENTRY(_rtld_dispatch_signal) mov c1, c27 mov c30, c24 b dispatch_signal_end -END(_rtld_dispatch_signal) +END(_rtld_dispatch_signal_unsafe) ENTRY(allocate_rstk) /* diff --git a/libexec/rtld-elf/cheri/cheri_reloc.h b/libexec/rtld-elf/cheri/cheri_reloc.h index cc916f01718b..911044981faf 100644 --- a/libexec/rtld-elf/cheri/cheri_reloc.h +++ b/libexec/rtld-elf/cheri/cheri_reloc.h @@ -119,14 +119,6 @@ process_r_cheri_capability(Obj_Entry *obj, Elf_Word r_symndx, } /* Remove write permissions and set bounds */ symval = make_function_cap_with_addend(def, defobj, addend); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(RTLD_SANDBOX) - symval = tramp_intern(obj, &(struct tramp_data) { - .target = __DECONST(void *, symval), - .defobj = defobj, - .def = def, - .sig = sigtab_get(obj, r_symndx) - }); -#endif if (__predict_false(symval == NULL)) { _rtld_error("Could not create function pointer for %s " "(in %s)\n", diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c index a64dbcad5513..c122a32a0c02 100644 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -238,6 +238,9 @@ static bool ld_skip_init_funcs = false; /* XXXAR: debug environment variable to static struct obj_entry_q obj_list; /* Queue of all loaded objects */ static Obj_Entry *obj_main; /* The main program shared object */ static Obj_Entry obj_rtld; /* The dynamic linker shared object */ +#if defined(__CHERI_PURE_CAPABILITY__) && defined(RTLD_SANDBOX) +Obj_Entry *obj_rtld_p = &obj_rtld; +#endif static unsigned int obj_count; /* Number of objects in obj_list */ static unsigned int obj_loads; /* Number of loads of objects (gen count) */ size_t ld_static_tls_extra = /* Static TLS extra space (bytes) */ @@ -1228,12 +1231,21 @@ _rtld_bind(Obj_Entry *obj, Elf_Size reloff) rtld_die(); if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) target = (uintptr_t)rtld_resolve_ifunc(defobj, def); - else + else { #ifdef __CHERI_PURE_CAPABILITY__ target = (uintptr_t)make_function_pointer(def, defobj); +#ifdef RTLD_SANDBOX + target = (uintptr_t)tramp_intern(obj, &(struct tramp_data) { + .target = (void *)target, + .defobj = defobj, + .def = def, + .sig = sigtab_get(obj, ELF_R_SYM(rel->r_info)) + }); +#endif #else target = (uintptr_t)(defobj->relocbase + def->st_value); #endif + } dbg("\"%s\" in \"%s\" ==> %p in \"%s\"", defobj->strtab + def->st_name, @@ -1248,14 +1260,6 @@ _rtld_bind(Obj_Entry *obj, Elf_Size reloff) * address. The value returned from reloc_jmpslot() is the value * that the trampoline needs. */ -#if defined(__CHERI_PURE_CAPABILITY__) && defined(RTLD_SANDBOX) - target = (uintptr_t)tramp_intern(obj, &(struct tramp_data) { - .target = (void *)target, - .defobj = defobj, - .def = def, - .sig = sigtab_get(obj, ELF_R_SYM(rel->r_info)) - }); -#endif target = reloc_jmpslot(where, target, defobj, obj, rel); lock_release(rtld_bind_lock, &lockstate); return (target); @@ -3293,6 +3297,13 @@ Obj_Entry * obj_from_addr(const void *addr) { Obj_Entry *obj; +#if defined(__CHERI_PURE_CAPABILITY__) && defined(RTLD_SANDBOX) + struct tramp_header *header; + + header = tramp_reflect(__DECONST(void *, addr)); + if (header != NULL) + return (__DECONST(Obj_Entry *, header->defobj)); +#endif TAILQ_FOREACH(obj, &obj_list, next) { if (obj->marker) @@ -4394,23 +4405,9 @@ 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)); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(RTLD_SANDBOX) - sym = tramp_intern(obj, &(struct tramp_data) { - .target = sym, - .defobj = defobj, - .def = def - }); -#endif dbg("dlsym(%s) is function: " PTR_FMT, name, sym); } else if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) { sym = rtld_resolve_ifunc(defobj, def); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(RTLD_SANDBOX) - sym = tramp_intern(obj, &(struct tramp_data) { - .target = sym, - .defobj = defobj, - .def = def - }); -#endif dbg("dlsym(%s) is ifunc. Resolved to: " PTR_FMT, name, sym); } else if (ELF_ST_TYPE(def->st_info) == STT_TLS) { ti.ti_module = defobj->tlsindex; diff --git a/libexec/rtld-elf/rtld_c18n.c b/libexec/rtld-elf/rtld_c18n.c index 3028d8586b58..8de9ad82ecf2 100644 --- a/libexec/rtld-elf/rtld_c18n.c +++ b/libexec/rtld-elf/rtld_c18n.c @@ -429,16 +429,10 @@ tramp_should_include(const Obj_Entry *reqobj, const struct tramp_data *data) { const char *sym; - /* - * INVARIANT: The target of each trampoline is tagged. - */ - if (!cheri_gettag(data->target)) - return (false); - - if (reqobj == NULL) + if (data->def == NULL) return (true); - if (reqobj->compart_id == data->defobj->compart_id) + if (data->def == &sym_zero) return (false); sym = strtab_value(data->defobj, data->def->st_name); @@ -446,6 +440,12 @@ tramp_should_include(const Obj_Entry *reqobj, const struct tramp_data *data) if (string_base_search(&uni_compart.trusts, sym) != -1) return (false); + if (reqobj == NULL) + return (true); + + if (reqobj->compart_id == data->defobj->compart_id) + return (false); + if (string_base_search(&comparts.data[reqobj->compart_id].trusts, sym) != -1) return (false); @@ -1169,9 +1169,28 @@ tramp_intern(const Obj_Entry *reqobj, const struct tramp_data *data) ptraddr_t key; int exp; - /* reqobj == NULL iff the request is by RTLD */ - assert((reqobj == NULL || data->def != NULL) && data->defobj != NULL - && func_sig_legal(data->sig)); + /* + * INVARIANT: The defobj of each trampoline is tagged. + */ + assert(cheri_gettag(data->defobj)); + if (data->def == NULL) + /* + * XXX-DG: reqobj != NULL causes policies to be evaluated which + * might result in a trampoline being elided. This is only safe + * to do for jump slot relocations. + * + * Currently, the decision to elide the trampoline or not is + * coupled with the decision of whether the symbol should be + * made accesible to the requesting object. This is insecure. + */ + assert(reqobj == NULL); + else if (data->def == &sym_zero) + assert(data->target == NULL); + else + assert(data->defobj->symtab <= data->def && + data->def < data->defobj->symtab + + data->defobj->dynsymcount); + assert(func_sig_legal(data->sig)); if (!tramp_should_include(reqobj, data)) return (data->target); @@ -1283,9 +1302,8 @@ tramp_intern(const Obj_Entry *reqobj, const struct tramp_data *data) struct func_sig sigtab_get(const Obj_Entry *obj, unsigned long symnum) { - if (symnum >= obj->dynsymcount) - rtld_fatal("Invalid symbol number %lu for object %s.", - symnum, obj->path); + rtld_require(symnum < obj->dynsymcount, + "c18n: Invalid symbol number %lu for object %s", symnum, obj->path); if (obj->sigtab == NULL) return ((struct func_sig) { .valid = false }); @@ -1293,27 +1311,31 @@ sigtab_get(const Obj_Entry *obj, unsigned long symnum) return (obj->sigtab[symnum]); } -static struct tramp_header * +struct tramp_header * tramp_reflect(void *entry) { - struct tramp_pg *page = atomic_load_explicit(&tramp_pgs.head, - memory_order_acquire); - uintptr_t data = (uintptr_t)entry; struct tramp_header *ret; - - if (!cheri_gettag(data)) + struct tramp_pg *page; + char *data = entry; + + 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) return (NULL); #ifndef __ARM_MORELLO_PURECAP_BENCHMARK_ABI data -= 1; #endif - data = (uintptr_t)__containerof((void *)data, struct tramp_header, - entry); + data = (char *)__containerof((void *)data, struct tramp_header, entry); + + page = atomic_load_explicit(&tramp_pgs.head, memory_order_acquire); while (page != NULL) { - ret = cheri_buildcap(page, data); + ret = cheri_buildcap(page, (uintptr_t)data); if (cheri_gettag(ret)) { - if (cheri_gettag(ret->target)) + if (cheri_gettag(ret->defobj)) /* * At this point, the provided data must have * been (a) tagged and (b) pointing to the entry @@ -1333,6 +1355,15 @@ tramp_reflect(void *entry) /* * APIs */ +/* + * XXX: Explicit function pointer used so that RTLD can wrap it in trampoline. + */ +extern void (*_rtld_sighandler)(int, siginfo_t *, void *); +extern void (*_rtld_dispatch_signal)(int, siginfo_t *, void *); + +void _rtld_sighandler_unsafe(int, siginfo_t *, void *); +void _rtld_dispatch_signal_unsafe(int, siginfo_t *, void *); + #define C18N_FUNC_SIG_COUNT 72 void @@ -1419,6 +1450,26 @@ c18n_init(void) atomic_store_explicit(&tramp_pgs.head, tramp_pg_new(NULL), memory_order_relaxed); + + /* + * Wrap the hooks used by external libraries in trampolines. + */ + _rtld_sighandler = tramp_intern(NULL, &(struct tramp_data) { + .target = _rtld_sighandler_unsafe, + .defobj = obj_rtld_p, + .sig = (struct func_sig) { + .valid = true, + .reg_args = 3, .mem_args = false, .ret_args = NONE + } + }); + _rtld_dispatch_signal = tramp_intern(NULL, &(struct tramp_data) { + .target = _rtld_dispatch_signal_unsafe, + .defobj = obj_rtld_p, + .sig = (struct func_sig) { + .valid = true, + .reg_args = 3, .mem_args = false, .ret_args = NONE + } + }); } void * @@ -1519,7 +1570,8 @@ _rtld_thr_exit(long *state) /* * Signal support */ -void _rtld_dispatch_signal(int, siginfo_t *, void *); +void (*_rtld_sighandler)(int, siginfo_t *, void *); +void (*_rtld_dispatch_signal)(int, siginfo_t *, void *); #ifndef __ARM_MORELLO_PURECAP_BENCHMARK_ABI ptraddr_t sighandler_fix_link(struct trusted_frame *, ucontext_t *); @@ -1624,14 +1676,14 @@ dispatch_signal_end(ucontext_t *new, ucontext_t *old __unused) extern __siginfohandler_t *signal_dispatcher; -__siginfohandler_t *signal_dispatcher = _rtld_dispatch_signal; +__siginfohandler_t *signal_dispatcher = _rtld_dispatch_signal_unsafe; void _rtld_sighandler_init(__siginfohandler_t *); void _rtld_sighandler_init(__siginfohandler_t *p) { - assert(signal_dispatcher == _rtld_dispatch_signal && + assert(signal_dispatcher == _rtld_dispatch_signal_unsafe && (cheri_getperm(p) & CHERI_PERM_EXECUTIVE) == 0); signal_dispatcher = tramp_intern(NULL, &(struct tramp_data) { .target = p, diff --git a/libexec/rtld-elf/rtld_c18n.h b/libexec/rtld-elf/rtld_c18n.h index 3fcf798a38c2..e26a29ae74b7 100644 --- a/libexec/rtld-elf/rtld_c18n.h +++ b/libexec/rtld-elf/rtld_c18n.h @@ -34,6 +34,8 @@ /* * Global symbols */ +extern Obj_Entry *obj_rtld_p; + extern uintptr_t sealer_pltgot, sealer_tramp; extern const char *ld_compartment_utrace; extern const char *ld_compartment_enable; @@ -192,6 +194,8 @@ void *tramp_intern(const Obj_Entry *reqobj, const struct tramp_data *); struct func_sig sigtab_get(const Obj_Entry *, unsigned long); +struct tramp_header *tramp_reflect(void *); + static inline long func_sig_to_otype(struct func_sig sig) { diff --git a/libexec/rtld-elf/rtld_c18n_policy.txt b/libexec/rtld-elf/rtld_c18n_policy.txt index 6200a01d1f72..d08dfbdbf8d9 100644 --- a/libexec/rtld-elf/rtld_c18n_policy.txt +++ b/libexec/rtld-elf/rtld_c18n_policy.txt @@ -54,7 +54,6 @@ trust execvp execvpe execvP - _rtld_thread_start callee [RTLD] export to [TCB]