diff --git a/lib/libc/aarch64/gen/_setjmp.S b/lib/libc/aarch64/gen/_setjmp.S index 66a239b563a3..624d9ff1430c 100644 --- a/lib/libc/aarch64/gen/_setjmp.S +++ b/lib/libc/aarch64/gen/_setjmp.S @@ -37,6 +37,13 @@ ENTRY(_setjmp) ldr x8, .Lmagic mov REG(9), REGN(sp) stp REG(8), REG(9), [REG(0)], #(REG_WIDTH * 2) +#ifdef CHERI_LIB_C18N + /* Store the trusted stack pointer */ + stp c0, c30, [csp, #-0x20]! + bl dl_c18n_get_trusted_stk + ldp c0, c30, [csp], #0x20 + add c0, c0, #REG_WIDTH +#endif /* Store the general purpose registers and lr */ stp REG(19), REG(20), [REG(0)], #(REG_WIDTH * 2) @@ -55,18 +62,8 @@ ENTRY(_setjmp) #endif /* Return value */ -#ifdef CHERI_LIB_C18N - mov c1, c0 -#endif mov x0, #0 -#ifdef CHERI_LIB_C18N - /* - * Tail-call to save Executive mode state - */ - b _rtld_setjmp -#else RETURN -#endif .align 3 .Lmagic: .quad _JB_MAGIC__SETJMP @@ -79,12 +76,26 @@ ENTRY(_longjmp) cmp x8, x9 b.ne botch +#ifdef CHERI_LIB_C18N + /* + * Preserve the arguments in callee-saved registers instead of pushing + * them onto the stack because stack unwinding will switch the stack. + */ + mov c19, c0 + mov c20, c1 + /* Pass the target untrusted stack pointer and trusted stack pointer */ + ldp c0, c1, [c0] + bl dl_c18n_unwind_trusted_stk + mov c0, c19 + mov c1, c20 +#endif + /* Restore the stack pointer */ ldr REG(8), [REG(0)], #(REG_WIDTH) -#ifdef CHERI_LIB_C18N - mov c2, c8 -#else mov REGN(sp), REG(8) +#ifdef CHERI_LIB_C18N + /* Skip the trusted stack pointer */ + add c0, c0, #REG_WIDTH #endif /* Restore the general purpose registers and lr */ @@ -104,19 +115,9 @@ ENTRY(_longjmp) #endif /* Load the return value */ -#ifdef CHERI_LIB_C18N - mov c3, c0 -#endif cmp x1, #0 csinc x0, x1, xzr, ne -#ifdef CHERI_LIB_C18N - /* - * Tail-call to restore Executive mode state - */ - b _rtld_longjmp -#else RETURN -#endif botch: #ifdef _STANDALONE diff --git a/lib/libc/aarch64/gen/setjmp.S b/lib/libc/aarch64/gen/setjmp.S index 164199ca695a..7931b8c03e24 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)] @@ -54,6 +49,13 @@ ENTRY(setjmp) ldr x8, .Lmagic mov REG(9), REGN(sp) stp REG(8), REG(9), [REG(0)], #(REG_WIDTH * 2) +#ifdef CHERI_LIB_C18N + /* Store the trusted stack pointer */ + stp c0, c30, [csp, #-0x20]! + bl dl_c18n_get_trusted_stk + ldp c0, c30, [csp], #0x20 + add c0, c0, #REG_WIDTH +#endif /* Store the general purpose registers and lr */ stp REG(19), REG(20), [REG(0)], #(REG_WIDTH * 2) @@ -70,24 +72,28 @@ ENTRY(setjmp) stp d14, d15, [REG(0)], #16 /* Return value */ -#ifdef CHERI_LIB_C18N - mov c1, c0 -#endif mov x0, #0 -#ifdef CHERI_LIB_C18N - /* - * Tail-call to save Executive mode state - */ - b _rtld_setjmp -#else RETURN -#endif .align 3 .Lmagic: .quad _JB_MAGIC_SETJMP END(setjmp) ENTRY(longjmp) +#ifdef CHERI_LIB_C18N + /* + * Preserve the arguments in callee-saved registers instead of pushing + * them onto the stack because stack unwinding will switch the stack. + */ + mov c19, c0 + mov c20, c1 + /* Pass the target untrusted stack pointer and trusted stack pointer */ + ldp c0, c1, [c0, #(REG_WIDTH * 1)] + bl dl_c18n_unwind_trusted_stk + mov c0, c19 + mov c1, c20 +#endif + sub REGN(sp), REGN(sp), #(REG_WIDTH * 4) stp REG(0), REGN(lr), [REGN(sp)] str REG(1), [REGN(sp), #(REG_WIDTH * 2)] @@ -110,10 +116,10 @@ ENTRY(longjmp) /* Restore the stack pointer */ ldr REG(8), [REG(0)], #(REG_WIDTH) -#ifdef CHERI_LIB_C18N - mov c2, c8 -#else mov REGN(sp), REG(8) +#ifdef CHERI_LIB_C18N + /* Skip the trusted stack pointer */ + add c0, c0, #REG_WIDTH #endif /* Restore the general purpose registers and lr */ @@ -131,19 +137,9 @@ ENTRY(longjmp) ldp d14, d15, [REG(0)], #16 /* Load the return value */ -#ifdef CHERI_LIB_C18N - mov c3, c0 -#endif cmp x1, #0 csinc x0, x1, xzr, ne -#ifdef CHERI_LIB_C18N - /* - * Tail-call to restore Executive mode state - */ - b _rtld_longjmp -#else RETURN -#endif botch: bl _C_LABEL(longjmperror) diff --git a/lib/libgcc_s/Makefile b/lib/libgcc_s/Makefile index 5cc573d2a042..4d46a07b96fb 100644 --- a/lib/libgcc_s/Makefile +++ b/lib/libgcc_s/Makefile @@ -53,10 +53,9 @@ SRCS+= s_logbl.c SRCS+= s_scalbnl.c .endif -# LIBUNWIND_SANDBOX_OTYPES is only supported on aarch64 (Morello). +# c18n is only supported on Morello. .if ${MACHINE_ABI:Mpurecap} && ${MACHINE_CPUARCH} == "aarch64" SYMBOL_MAPS+= ${.CURDIR}/Symbol-c18n.map -CFLAGS+= -D_LIBUNWIND_CHERI_C18N_SUPPORT .endif .include diff --git a/lib/libgcc_s/Symbol-c18n.map b/lib/libgcc_s/Symbol-c18n.map index 6cddda834561..0054a7e586d1 100644 --- a/lib/libgcc_s/Symbol-c18n.map +++ b/lib/libgcc_s/Symbol-c18n.map @@ -1,5 +1,6 @@ FBSDprivate_1.0 { - _rtld_unw_getcontext; - _rtld_unw_setcontext; - _rtld_unw_getsealer; + dl_c18n_get_trusted_stk; + dl_c18n_unwind_trusted_stk; + dl_c18n_is_tramp; + dl_c18n_pop_trusted_stk; }; diff --git a/libexec/rtld-elf/Symbol-c18n.map b/libexec/rtld-elf/Symbol-c18n.map index 3729640162b9..dbbb00ccbc04 100644 --- a/libexec/rtld-elf/Symbol-c18n.map +++ b/libexec/rtld-elf/Symbol-c18n.map @@ -11,10 +11,10 @@ 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; + dl_c18n_get_trusted_stk; + dl_c18n_unwind_trusted_stk; + dl_c18n_is_tramp; + dl_c18n_pop_trusted_stk; }; diff --git a/libexec/rtld-elf/aarch64/rtld_c18n_asm.S b/libexec/rtld-elf/aarch64/rtld_c18n_asm.S index 17b699f5c533..0fe1e076f117 100644 --- a/libexec/rtld-elf/aarch64/rtld_c18n_asm.S +++ b/libexec/rtld-elf/aarch64/rtld_c18n_asm.S @@ -34,6 +34,9 @@ * See rtld_c18n.h for an overview of the design. */ +/* + * XXX: These assembly stubs are kept here for compatibility with old libunwind. + */ /* * The _rtld_unw_{get,set}context_epilogue functions are stack unwinding * helpers. See the 'Stack unwinding' section in rtld_c18n.c. diff --git a/libexec/rtld-elf/aarch64/rtld_c18n_machdep.h b/libexec/rtld-elf/aarch64/rtld_c18n_machdep.h index 5e2607f42f08..4d2f0335d5d3 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 dl_c18n_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 1eecfac7c9aa..1ebecf5f0fff 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,6 +858,8 @@ resolve_untrusted_stk_impl(stk_table_index index) /* * Stack unwinding + * + * APIs exposed to stack unwinders (e.g., libc setjmp/longjmp and libunwind) */ /* * Assembly functions that are tail-called when compartmentalisation is @@ -867,8 +868,8 @@ resolve_untrusted_stk_impl(stk_table_index index) 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) +void +dl_c18n_get_trusted_stk(void **buf) { /* * This helper is used by functions like setjmp. Before setjmp is @@ -882,17 +883,23 @@ unwind_cursor(void) * buffer. */ - return (get_trusted_stk()->previous); + if (C18N_ENABLED) + *buf = cheri_seal(get_trusted_stk()->previous, + sealer_trusted_stk); + else + *buf = NULL; } +/* + * XXX: These functions are kept here for compatibility with old libunwind. + */ 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); + dl_c18n_get_trusted_stk(buf); return (ret); } @@ -903,7 +910,7 @@ _rtld_unw_getcontext(uintptr_t ret, void **buf) __attribute__((musttail)) return (_rtld_unw_getcontext_epilogue(ret, buf)); } - *buf = cheri_seal(unwind_cursor(), sealer_unwbuf); + dl_c18n_get_trusted_stk(buf); return (ret); } @@ -913,8 +920,8 @@ _rtld_unw_getcontext(uintptr_t ret, void **buf) */ 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) +void +dl_c18n_unwind_trusted_stk(void *rcsp, void *target) { /* * This helper is used by functions like longjmp. Before longjmp is @@ -936,6 +943,9 @@ unwind_stack(struct jmp_args ret, void *rcsp, struct trusted_frame *target) struct trusted_frame *cur, *tf; sigset_t nset, oset; + if (!C18N_ENABLED) + return; + /* * Make the function re-entrant by blocking all signals. */ @@ -943,6 +953,7 @@ unwind_stack(struct jmp_args 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) || (ptraddr_t)tf->previous >= (ptraddr_t)target) { @@ -991,36 +1002,64 @@ unwind_stack(struct jmp_args ret, void *rcsp, struct trusted_frame *target) abort(); } - tf->sp = rcsp; + tf->state.sp = rcsp; tf->osp = *ospp; tf->previous = cur; tf->caller = index; sigprocmask(SIG_SETMASK, &oset, NULL); +} - return (ret); +int +dl_c18n_is_tramp(ptraddr_t pc, void *tfs) +{ + struct trusted_frame *tf; + + if (!C18N_ENABLED) + return (0); + + tf = cheri_unseal(tfs, sealer_trusted_stk); + return (pc == tf->landing); +} + +void * +dl_c18n_pop_trusted_stk(struct dl_c18n_compart_state *state, void *tfs) +{ + struct trusted_frame *tf; + + if (!C18N_ENABLED) + return (NULL); + + tf = cheri_unseal(tfs, sealer_trusted_stk); + *state = tf->state; + return (cheri_seal(tf->previous, sealer_trusted_stk)); } +/* + * XXX: These functions are kept here for compatibility with old libunwind. + */ struct jmp_args _rtld_longjmp(struct jmp_args, void *, void **); struct jmp_args _rtld_unw_setcontext_impl(struct jmp_args, void *, void **); struct jmp_args _rtld_longjmp(struct jmp_args ret, void *rcsp, void **buf) { - return (unwind_stack(ret, rcsp, cheri_unseal(*buf, sealer_jmpbuf))); + dl_c18n_unwind_trusted_stk(rcsp, *buf); + return (ret); } struct jmp_args _rtld_unw_setcontext_impl(struct jmp_args ret, void *rcsp, void **buf) { - return (unwind_stack(ret, rcsp, cheri_unseal(*buf, sealer_unwbuf))); + dl_c18n_unwind_trusted_stk(rcsp, *buf); + return (ret); } uintptr_t _rtld_unw_getsealer(void); uintptr_t _rtld_unw_getsealer(void) { - return (sealer_unwbuf); + return (sealer_trusted_stk); } /* @@ -1210,9 +1249,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)); @@ -1639,10 +1678,7 @@ c18n_init2(Obj_Entry *obj_rtld) 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); @@ -1978,7 +2014,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 dl_c18n_compart_state) { + .sp = nsp + }, .osp = osp, .previous = tf, .caller = intr_idx, @@ -2033,7 +2071,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 646d48b23779..1c6a17bf7b3d 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 dl_c18n_compart_state state; + /* + * INVARIANT: This field contains the top of the caller's stack when the + * caller was last entered. + */ + void *osp; + /* + * Pointer to the previous trusted frame + */ + struct trusted_frame *previous; + /* + * Stack table index of the caller, derived from its compartment ID + */ + 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; + /* + * Stack table index of the callee, derived from its compartment ID + */ + 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..4b085a871c31 100644 --- a/libexec/rtld-elf/rtld_c18n_policy.txt +++ b/libexec/rtld-elf/rtld_c18n_policy.txt @@ -59,8 +59,16 @@ export to [TCB] _rtld_setjmp _rtld_longjmp +callee [RTLD] +export to [TCB] +export to [libunwind] + dl_c18n_get_trusted_stk + dl_c18n_unwind_trusted_stk + callee [RTLD] export to [libunwind] _rtld_unw_getcontext _rtld_unw_setcontext _rtld_unw_getsealer + dl_c18n_is_tramp + dl_c18n_pop_trusted_stk diff --git a/sys/sys/link_elf.h b/sys/sys/link_elf.h index f999a296d945..0c1e365acf98 100644 --- a/sys/sys/link_elf.h +++ b/sys/sys/link_elf.h @@ -110,6 +110,12 @@ int _rtld_addr_phdr(const void *, struct dl_phdr_info *); int _rtld_get_stack_prot(void); int _rtld_is_dlopened(void *); +struct dl_c18n_compart_state; +void dl_c18n_get_trusted_stk(void **); +void dl_c18n_unwind_trusted_stk(void *, void *); +int dl_c18n_is_tramp(ptraddr_t, void *); +void *dl_c18n_pop_trusted_stk(struct dl_c18n_compart_state *, void *); + #ifdef __ARM_EABI__ void * dl_unwind_find_exidx(const void *, int *); #endif