diff --git a/libexec/rtld-elf/aarch64/reloc.c b/libexec/rtld-elf/aarch64/reloc.c index 3afcad7adbb3..cefa7f1fa9e7 100644 --- a/libexec/rtld-elf/aarch64/reloc.c +++ b/libexec/rtld-elf/aarch64/reloc.c @@ -898,17 +898,44 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags, return (-1); break; case R_MORELLO_RELATIVE: + *(uintcap_t *)(void *)where = + init_cap_from_fragment(where, data_cap, + text_rodata_cap, + (Elf_Addr)(uintptr_t)obj->relocbase, + rela->r_addend); + break; case R_MORELLO_FUNC_RELATIVE: *(uintcap_t *)(void *)where = init_cap_from_fragment(where, data_cap, text_rodata_cap, (Elf_Addr)(uintptr_t)obj->relocbase, rela->r_addend); +#ifdef CHERI_LIB_C18N + if (C18N_FPTR_ENABLED) + *(void **)where = tramp_intern(NULL, + &(struct tramp_data) { + .target = *(void **)where, + .defobj = obj + }); +#endif break; #endif /* __has_feature(capabilities) */ case R_AARCH64_ABS64: case R_AARCH64_GLOB_DAT: *where = symval + rela->r_addend; +#ifdef CHERI_LIB_C18N + if (C18N_FPTR_ENABLED && + (ELF_ST_TYPE(def->st_info) == STT_FUNC || + ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC)) + *where = (Elf_Addr)tramp_intern(NULL, + &(struct tramp_data) { + .target = (void *)(uintptr_t)*where, + .defobj = defobj, + .def = def, + .sig = sigtab_get(obj, + ELF_R_SYM(rela->r_info)) + }); +#endif break; case R_AARCH64_COPY: /* @@ -976,8 +1003,18 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags, break; #endif case R_AARCH64_RELATIVE: + *where = (Elf_Addr)(obj->relocbase + rela->r_addend); + break; case R_AARCH64_FUNC_RELATIVE: *where = (Elf_Addr)(obj->relocbase + rela->r_addend); +#ifdef CHERI_LIB_C18N + if (C18N_FPTR_ENABLED) + *where = (Elf_Addr)tramp_intern(NULL, + &(struct tramp_data) { + .target = (void *)(uintptr_t)*where, + .defobj = obj + }); +#endif break; case R_AARCH64_NONE: break; diff --git a/libexec/rtld-elf/aarch64/rtld_machdep.h b/libexec/rtld-elf/aarch64/rtld_machdep.h index b7a736d6d515..3aba16fff18f 100644 --- a/libexec/rtld-elf/aarch64/rtld_machdep.h +++ b/libexec/rtld-elf/aarch64/rtld_machdep.h @@ -84,20 +84,22 @@ uintptr_t reloc_jmpslot(uintptr_t *where, uintptr_t target, /* TODO: Per-function captable/PLT/FNDESC support */ #ifdef CHERI_LIB_C18N #define call_init_array_pointer(_obj, _target) \ - (((InitArrFunc)tramp_intern(NULL, &(struct tramp_data) { \ + (C18N_FPTR_ENABLED ? (InitArrFunc)(_target).value : \ + (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)) + }))(main_argc, main_argv, environ) #define call_fini_array_pointer(_obj, _target) \ - (((InitFunc)tramp_intern(NULL, &(struct tramp_data) { \ + (C18N_FPTR_ENABLED ? (InitFunc)(_target).value : \ + (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)) diff --git a/libexec/rtld-elf/cheri/cheri_reloc.h b/libexec/rtld-elf/cheri/cheri_reloc.h index b432ba49dcac..f4ed3b86fc84 100644 --- a/libexec/rtld-elf/cheri/cheri_reloc.h +++ b/libexec/rtld-elf/cheri/cheri_reloc.h @@ -125,13 +125,16 @@ 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) - }); +#ifdef CHERI_LIB_C18N +#ifndef __riscv + if (C18N_FPTR_ENABLED) +#endif + 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 */ diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c index 0095714d342c..2e5a950184cc 100644 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -415,6 +415,7 @@ static struct ld_env_var_desc ld_env_vars[] = { LD_ENV_DESC(COMPARTMENT_UNWIND, false), LD_ENV_DESC(COMPARTMENT_STATS, false), LD_ENV_DESC(COMPARTMENT_SWITCH_COUNT, false), + LD_ENV_DESC(COMPARTMENT_FPTR, false), #endif }; @@ -821,6 +822,7 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) ld_compartment_unwind = ld_get_env_var(LD_COMPARTMENT_UNWIND); ld_compartment_stats = ld_get_env_var(LD_COMPARTMENT_STATS); ld_compartment_switch_count = ld_get_env_var(LD_COMPARTMENT_SWITCH_COUNT); + ld_compartment_fptr = ld_get_env_var(LD_COMPARTMENT_FPTR) != NULL; /* * DISABLE takes precedence over ENABLE. */ @@ -1142,19 +1144,8 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) dbg("transferring control to program entry point = " PTR_FMT, obj_main->entry); /* Return the exit procedure and the program entry point. */ - if (rtld_exit_ptr == NULL) { + if (rtld_exit_ptr == NULL) rtld_exit_ptr = make_rtld_function_pointer(rtld_exit); -#ifdef CHERI_LIB_C18N - rtld_exit_ptr = tramp_intern(NULL, &(struct tramp_data) { - .target = rtld_exit_ptr, - .defobj = &obj_rtld, - .sig = (struct func_sig) { - .valid = true, - .reg_args = 0, .mem_args = false, .ret_args = NONE - } - }); -#endif - } *exit_proc = rtld_exit_ptr; *objp = obj_main; @@ -1225,9 +1216,18 @@ _rtld_bind(Obj_Entry *obj, Elf_Size reloff) NULL, &lockstate); if (def == NULL) rtld_die(); - if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) + if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) { target = (uintptr_t)rtld_resolve_ifunc(defobj, def); - else { +#ifdef CHERI_LIB_C18N + if (C18N_FPTR_ENABLED) + 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 { #ifdef __CHERI_PURE_CAPABILITY__ target = (uintptr_t)make_function_pointer(def, defobj); #ifdef CHERI_LIB_C18N @@ -3509,29 +3509,9 @@ 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); -#ifdef CHERI_LIB_C18N - exit_ptr = tramp_intern(NULL, &(struct tramp_data) { - .target = exit_ptr, - .defobj = &obj_rtld, - .sig = (struct func_sig) { - .valid = true, - .reg_args = 0, .mem_args = false, .ret_args = NONE - } - }); -#endif dbg("Calling __libc_atexit(rtld_exit (" PTR_FMT "))", (void*)exit_ptr); reg(exit_ptr); rtld_exit_ptr = make_rtld_function_pointer(rtld_nop_exit); -#ifdef CHERI_LIB_C18N - rtld_exit_ptr = tramp_intern(NULL, &(struct tramp_data) { - .target = rtld_exit_ptr, - .defobj = &obj_rtld, - .sig = (struct func_sig) { - .valid = true, - .reg_args = 0, .mem_args = false, .ret_args = NONE - } - }); -#endif } /* @@ -4451,22 +4431,28 @@ 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 - }); +#ifdef CHERI_LIB_C18N +#ifndef __riscv + if (C18N_FPTR_ENABLED) +#endif + 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 - }); +#ifdef CHERI_LIB_C18N +#ifndef __riscv + if (C18N_FPTR_ENABLED) +#endif + 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; @@ -4704,14 +4690,16 @@ dl_iterate_phdr(__dl_iterate_hdr_callback callback, void *param) error = 0; #ifdef CHERI_LIB_C18N - callback = tramp_intern(NULL, &(struct tramp_data) { - .target = callback, - .defobj = obj_from_addr(callback), - .sig = (struct func_sig) { - .valid = true, - .reg_args = 3, .mem_args = false, .ret_args = ONE - } - }); + if (!C18N_FPTR_ENABLED) + callback = tramp_intern(NULL, &(struct tramp_data) { + .target = callback, + .defobj = obj_from_addr(callback), + .sig = (struct func_sig) { + .valid = true, + .reg_args = 3, .mem_args = false, + .ret_args = ONE + } + }); #endif wlock_acquire(rtld_phdr_lock, &phdr_lockstate); diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h index 5667192dd5d1..024435514d99 100644 --- a/libexec/rtld-elf/rtld.h +++ b/libexec/rtld-elf/rtld.h @@ -65,6 +65,14 @@ extern bool ld_compartment_enable; #define C18N_ENABLED ld_compartment_enable + +#ifdef __aarch64__ +extern bool ld_compartment_fptr; + +#define C18N_FPTR_ENABLED ld_compartment_fptr +#else +#define C18N_FPTR_ENABLED 0 +#endif #endif #include "rtld_lock.h" @@ -479,6 +487,7 @@ enum { LD_COMPARTMENT_UNWIND, LD_COMPARTMENT_STATS, LD_COMPARTMENT_SWITCH_COUNT, + LD_COMPARTMENT_FPTR, #endif }; @@ -546,8 +555,21 @@ __END_DECLS #endif #ifndef make_rtld_function_pointer +#ifdef CHERI_LIB_C18N +#define make_rtld_function_pointer(target_func) \ + tramp_intern(NULL, &(struct tramp_data) { \ + .target = &target_func, \ + .defobj = &obj_rtld, \ + .sig = (struct func_sig) { \ + .valid = true, \ + .reg_args = 0, .mem_args = false, \ + .ret_args = NONE \ + } \ + }) +#else #define make_rtld_function_pointer(target_func) (&target_func) #endif +#endif #ifndef make_rtld_local_function_pointer #define make_rtld_local_function_pointer(target_func) (&target_func) #endif diff --git a/libexec/rtld-elf/rtld_c18n.c b/libexec/rtld-elf/rtld_c18n.c index 69ad597e45b2..d801f8befaed 100644 --- a/libexec/rtld-elf/rtld_c18n.c +++ b/libexec/rtld-elf/rtld_c18n.c @@ -136,6 +136,11 @@ uintptr_t sealer_tidc; /* Enable compartmentalisation */ bool ld_compartment_enable; +#ifdef __aarch64__ +/* Enable wrapping function pointers in trampolines */ +bool ld_compartment_fptr; +#endif + /* Use utrace() to log compartmentalisation-related events */ const char *ld_compartment_utrace; @@ -1933,17 +1938,21 @@ void _rtld_thread_start_init(void (*p)(struct pthread *)) { #ifdef HAS_RESTRICTED_MODE - assert((cheri_getperm(p) & CHERI_PERM_EXECUTIVE) == 0); + assert(((cheri_getperm(p) & CHERI_PERM_EXECUTIVE) != 0) == + C18N_FPTR_ENABLED); #endif assert(thr_thread_start == NULL); - thr_thread_start = tramp_intern(NULL, &(struct tramp_data) { - .target = p, - .defobj = obj_from_addr(p), - .sig = (struct func_sig) { - .valid = true, - .reg_args = 1, .mem_args = false, .ret_args = NONE - } - }); + if (!C18N_FPTR_ENABLED) + p = tramp_intern(NULL, &(struct tramp_data) { + .target = p, + .defobj = obj_from_addr(p), + .sig = (struct func_sig) { + .valid = true, + .reg_args = 1, .mem_args = false, + .ret_args = NONE + } + }); + thr_thread_start = p; } void @@ -2096,17 +2105,21 @@ void _rtld_sighandler_init(__siginfohandler_t *handler) { #ifdef HAS_RESTRICTED_MODE - assert((cheri_getperm(handler) & CHERI_PERM_EXECUTIVE) == 0); + assert(((cheri_getperm(handler) & CHERI_PERM_EXECUTIVE) != 0) == + C18N_FPTR_ENABLED); #endif assert(signal_dispatcher == sigdispatch); - signal_dispatcher = tramp_intern(NULL, &(struct tramp_data) { - .target = handler, - .defobj = obj_from_addr(handler), - .sig = (struct func_sig) { - .valid = true, - .reg_args = 3, .mem_args = false, .ret_args = NONE - } - }); + if (!C18N_FPTR_ENABLED) + handler = tramp_intern(NULL, &(struct tramp_data) { + .target = handler, + .defobj = obj_from_addr(handler), + .sig = (struct func_sig) { + .valid = true, + .reg_args = 3, .mem_args = false, + .ret_args = NONE + } + }); + signal_dispatcher = handler; } #ifndef USE_RESTRICTED_MODE @@ -2327,7 +2340,15 @@ _rtld_siginvoke(int sig, siginfo_t *info, ucontext_t *ucp, sigfunc = act->sa_sigaction; else sigfunc = act->sa_handler; - if (!cheri_gettag(sigfunc)) { + + header = tramp_reflect(sigfunc); + + /* + * The signal handler must be wrapped by a trampoline if function + * pointer wrapping is enabled. + */ + if (!cheri_gettag(sigfunc) || + (C18N_FPTR_ENABLED && header == NULL)) { rtld_fdprintf(STDERR_FILENO, "c18n: Invalid handler %#p for signal %d\n", sigfunc, sig); @@ -2338,8 +2359,7 @@ _rtld_siginvoke(int sig, siginfo_t *info, ucontext_t *ucp, * If the signal handler is not already wrapped by a trampoline, wrap it * in one. */ - header = tramp_reflect(sigfunc); - if (header == NULL) { + if (!C18N_FPTR_ENABLED && header == NULL) { defobj = obj_from_addr(sigfunc); sigfunc = tramp_intern(NULL, &(struct tramp_data) { .target = sigfunc, diff --git a/libexec/rtld-elf/rtld_c18n.h b/libexec/rtld-elf/rtld_c18n.h index 6a75a3ab8c5e..5d9e9d73c988 100644 --- a/libexec/rtld-elf/rtld_c18n.h +++ b/libexec/rtld-elf/rtld_c18n.h @@ -58,6 +58,7 @@ #define c18n_unseal_subset(cap, sealer, super) cheri_unseal(cap, sealer) #endif +extern bool ld_compartment_enable; #ifdef HAS_RESTRICTED_MODE extern size_t c18n_code_perm_clear; #else diff --git a/libexec/rtld-elf/rtld_lock.c b/libexec/rtld-elf/rtld_lock.c index 7194cdf0b575..140c94985543 100644 --- a/libexec/rtld-elf/rtld_lock.c +++ b/libexec/rtld-elf/rtld_lock.c @@ -427,16 +427,18 @@ _rtld_thread_init(struct RtldLockInfo *pli) } #ifdef CHERI_LIB_C18N tmplockinfo = *pli; -#define WRAP(_target, _valid, _reg_args, _mem_args, _ret_args) \ - _target = tramp_intern(NULL, &(struct tramp_data) { \ - .target = _target, \ - .defobj = obj, \ - .sig = (struct func_sig) { \ - .valid = _valid, \ - .reg_args = _reg_args, .mem_args = _mem_args, \ - .ret_args = _ret_args \ - } \ - }) +#define WRAP(_target, _valid, _reg_args, _mem_args, _ret_args) \ + do if (!C18N_FPTR_ENABLED) \ + _target = tramp_intern(NULL, &(struct tramp_data) { \ + .target = _target, \ + .defobj = obj, \ + .sig = (struct func_sig) { \ + .valid = _valid, \ + .reg_args = _reg_args, .mem_args = _mem_args, \ + .ret_args = _ret_args \ + } \ + }); \ + while (0) WRAP(tmplockinfo.lock_create, true, 0, false, ONE); WRAP(tmplockinfo.lock_destroy, true, 1, false, NONE); WRAP(tmplockinfo.rlock_acquire, true, 1, false, NONE);