From 5a00523819b9e1f0e590655be2cdba342b079ff9 Mon Sep 17 00:00:00 2001 From: Dapeng Gao Date: Wed, 28 Aug 2024 16:44:19 +0100 Subject: [PATCH 1/2] c18n: Compare pointers by converting them to integers first Using relational operators to compare pointers to different trusted frames is undefined behaviour. --- libexec/rtld-elf/rtld_c18n.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/libexec/rtld-elf/rtld_c18n.c b/libexec/rtld-elf/rtld_c18n.c index 04e454ce090c..2f2e0bed7ae2 100644 --- a/libexec/rtld-elf/rtld_c18n.c +++ b/libexec/rtld-elf/rtld_c18n.c @@ -945,7 +945,8 @@ unwind_stack(struct jmp_args ret, void *rcsp, struct trusted_frame *target) tf = get_trusted_stk(); - if (!cheri_is_subset(tf, target) || tf->previous >= target) { + if (!cheri_is_subset(tf, target) || + (ptraddr_t)tf->previous >= (ptraddr_t)target) { rtld_fdprintf(STDERR_FILENO, "c18n: Illegal unwind from %#p to %#p\n", tf, target); abort(); @@ -961,7 +962,7 @@ unwind_stack(struct jmp_args ret, void *rcsp, struct trusted_frame *target) cid = index_to_cid(index); ospp = &table->entries[cid].stack; - if (*ospp > cur->osp) { + if ((ptraddr_t)*ospp > (ptraddr_t)cur->osp) { rtld_fdprintf(STDERR_FILENO, "c18n: Cannot unwind %s from %#p to %#p\n", comparts.data[cid].name, *ospp, cur->osp); @@ -970,9 +971,9 @@ unwind_stack(struct jmp_args ret, void *rcsp, struct trusted_frame *target) *ospp = cur->osp; cur = cur->previous; - } while (cur < target); + } while ((ptraddr_t)cur < (ptraddr_t)target); - if (cur != target) { + if ((ptraddr_t)cur != (ptraddr_t)target) { rtld_fdprintf(STDERR_FILENO, "c18n: Illegal unwind from %#p to %#p\n", cur, target); abort(); @@ -983,7 +984,7 @@ unwind_stack(struct jmp_args ret, void *rcsp, struct trusted_frame *target) * topmost trusted frame to restore the untrusted stack when it is * popped. */ - if (rcsp > *ospp) { + if ((ptraddr_t)rcsp > (ptraddr_t)*ospp) { rtld_fdprintf(STDERR_FILENO, "c18n: Cannot complete unwind %s from %#p to %#p, ", "tf: %#p -> %#p\n", comparts.data[cid].name, rcsp, *ospp, From 08300c19981c6efda9944935a988dca7743cb733 Mon Sep 17 00:00:00 2001 From: Dapeng Gao Date: Tue, 18 Jun 2024 16:33:34 +0100 Subject: [PATCH 2/2] c18n: Expose unified unwinding APIs to setjmp/longjmp and libunwind 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. The unified unwinding APIs are declared as dl_c18n_* functions in link.h. setjmp/longjmp have been updated to use them. The previous API has been retained for compatibility with old libunwind. --- lib/libc/aarch64/gen/_setjmp.S | 49 +++---- lib/libc/aarch64/gen/setjmp.S | 54 ++++---- lib/libc/gen/Symbol.map | 6 + lib/libc/gen/dlfcn.c | 26 ++++ lib/libgcc_s/Makefile | 4 +- lib/libgcc_s/Symbol-c18n.map | 5 - libexec/rtld-elf/Symbol-c18n.map | 11 +- libexec/rtld-elf/aarch64/rtld_c18n_asm.S | 4 + libexec/rtld-elf/aarch64/rtld_c18n_machdep.h | 46 ------- libexec/rtld-elf/rtld_c18n.c | 133 +++++++++++++------ libexec/rtld-elf/rtld_c18n.h | 53 +++++++- libexec/rtld-elf/rtld_c18n_policy.txt | 8 ++ sys/arm64/include/c18n.h | 45 +++++++ sys/sys/link_elf.h | 8 ++ 14 files changed, 297 insertions(+), 155 deletions(-) delete mode 100644 lib/libgcc_s/Symbol-c18n.map create mode 100644 sys/arm64/include/c18n.h diff --git a/lib/libc/aarch64/gen/_setjmp.S b/lib/libc/aarch64/gen/_setjmp.S index 66a239b563a3..076290192edf 100644 --- a/lib/libc/aarch64/gen/_setjmp.S +++ b/lib/libc/aarch64/gen/_setjmp.S @@ -37,6 +37,15 @@ 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]! + mov c0, c30 + bl dl_c18n_get_trusted_stack + ldp c1, c30, [csp], #0x20 + str c0, [c1], #REG_WIDTH + mov c0, c1 +#endif /* Store the general purpose registers and lr */ stp REG(19), REG(20), [REG(0)], #(REG_WIDTH * 2) @@ -55,18 +64,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 +78,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_stack + 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 +117,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..bc5fefb478ce 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,15 @@ 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]! + mov c0, c30 + bl dl_c18n_get_trusted_stack + ldp c1, c30, [csp], #0x20 + str c0, [c1], #REG_WIDTH + mov c0, c1 +#endif /* Store the general purpose registers and lr */ stp REG(19), REG(20), [REG(0)], #(REG_WIDTH * 2) @@ -70,24 +74,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_stack + 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 +118,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 +139,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/libc/gen/Symbol.map b/lib/libc/gen/Symbol.map index 5906352dbe4b..79560e2e36cb 100644 --- a/lib/libc/gen/Symbol.map +++ b/lib/libc/gen/Symbol.map @@ -81,6 +81,12 @@ FBSD_1.0 { dlvsym; dlinfo; dl_iterate_phdr; +#if defined(__CHERI_PURE_CAPABILITY__) && defined(__aarch64__) + dl_c18n_get_trusted_stack; + dl_c18n_unwind_trusted_stack; + dl_c18n_is_trampoline; + dl_c18n_pop_trusted_stack; +#endif drand48; erand48; err_set_file; diff --git a/lib/libc/gen/dlfcn.c b/lib/libc/gen/dlfcn.c index 4df00f636742..8d869ebc8a44 100644 --- a/lib/libc/gen/dlfcn.c +++ b/lib/libc/gen/dlfcn.c @@ -377,4 +377,30 @@ _rtld_is_dlopened(void *arg __unused) return (0); } +#if defined(__CHERI_PURE_CAPABILITY__) && defined(__aarch64__) +#pragma weak dl_c18n_get_trusted_stack +void * +dl_c18n_get_trusted_stack(uintptr_t pc __unused) { + return (NULL); +} + +#pragma weak dl_c18n_unwind_trusted_stack +void +dl_c18n_unwind_trusted_stack(void *sp __unused, void *target __unused) { +} + +#pragma weak dl_c18n_is_trampoline +int +dl_c18n_is_trampoline(uintptr_t pc __unused, void *tfs __unused) { + return (0); +} + +#pragma weak dl_c18n_pop_trusted_stack +void * +dl_c18n_pop_trusted_stack(struct dl_c18n_compart_state *state __unused, + void *tfs __unused) { + return (NULL); +} +#endif + #endif /* !defined(IN_LIBDL) || defined(PIC) */ diff --git a/lib/libgcc_s/Makefile b/lib/libgcc_s/Makefile index 5cc573d2a042..2ff786dad0ad 100644 --- a/lib/libgcc_s/Makefile +++ b/lib/libgcc_s/Makefile @@ -53,10 +53,8 @@ SRCS+= s_logbl.c SRCS+= s_scalbnl.c .endif -# LIBUNWIND_SANDBOX_OTYPES is only supported on aarch64 (Morello). .if ${MACHINE_ABI:Mpurecap} && ${MACHINE_CPUARCH} == "aarch64" -SYMBOL_MAPS+= ${.CURDIR}/Symbol-c18n.map -CFLAGS+= -D_LIBUNWIND_CHERI_C18N_SUPPORT +CFLAGS+= -D_LIBUNWIND_HAS_CHERI_LIB_C18N .endif .include diff --git a/lib/libgcc_s/Symbol-c18n.map b/lib/libgcc_s/Symbol-c18n.map deleted file mode 100644 index 6cddda834561..000000000000 --- a/lib/libgcc_s/Symbol-c18n.map +++ /dev/null @@ -1,5 +0,0 @@ -FBSDprivate_1.0 { - _rtld_unw_getcontext; - _rtld_unw_setcontext; - _rtld_unw_getsealer; -}; diff --git a/libexec/rtld-elf/Symbol-c18n.map b/libexec/rtld-elf/Symbol-c18n.map index 3729640162b9..22a6a97e1f1b 100644 --- a/libexec/rtld-elf/Symbol-c18n.map +++ b/libexec/rtld-elf/Symbol-c18n.map @@ -11,10 +11,13 @@ 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; +}; + +FBSD_1.0 { + dl_c18n_get_trusted_stack; + dl_c18n_unwind_trusted_stack; + dl_c18n_is_trampoline; + dl_c18n_pop_trusted_stack; }; diff --git a/libexec/rtld-elf/aarch64/rtld_c18n_asm.S b/libexec/rtld-elf/aarch64/rtld_c18n_asm.S index 17b699f5c533..eb25aa0ef4c8 100644 --- a/libexec/rtld-elf/aarch64/rtld_c18n_asm.S +++ b/libexec/rtld-elf/aarch64/rtld_c18n_asm.S @@ -34,6 +34,10 @@ * See rtld_c18n.h for an overview of the design. */ +/* + * XXX Dapeng: These assembly stubs are kept here for compatibility with old + * libc and 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..4ed6a5f4030b 100644 --- a/libexec/rtld-elf/aarch64/rtld_c18n_machdep.h +++ b/libexec/rtld-elf/aarch64/rtld_c18n_machdep.h @@ -132,51 +132,5 @@ set_untrusted_stk(const void *sp) asm ("msr " __XSTRING(UNTRUSTED_STACK) ", %0" :: "C" (sp)); } #endif - -struct trusted_frame { - void *fp; - void *pc; - /* - * c19 to c28 - */ - void *regs[10]; - /* - * INVARIANT: This field contains the top of the caller's stack when the - * 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 2f2e0bed7ae2..a698ebbed9ef 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; @@ -860,6 +859,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 @@ -868,32 +869,47 @@ 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) +int +c18n_is_tramp(uintptr_t pc, const struct trusted_frame *tf) +{ + if (!cheri_gettag(pc)) + return (0); + return (pc == tf->landing); +} + +void * +dl_c18n_get_trusted_stack(uintptr_t pc) { /* - * This helper is used by functions like setjmp. Before setjmp is - * called, the top of the trusted stack contains: - * 0. Link to previous frame - * setjmp does not push to the trusted stack. When _rtld_setjmp is - * called, the following are pushed to the trusted stack: - * 1. Caller's data - * 2. Link to 0 - * We store a sealed capability to the caller's frame in the jump - * buffer. + * Return a sealed capability to the caller's trusted frame. But if the + * caller is entered via a trampoline and a return capability to said + * trampoline is passed as the argument, return a sealed capability to + * the trusted frame of the caller's own caller. */ - return (get_trusted_stk()->previous); + struct trusted_frame *tf; + + if (!C18N_ENABLED) + return (NULL); + + tf = get_trusted_stk()->previous; + if (c18n_is_tramp(pc, tf)) + tf = tf->previous; + + return (cheri_seal(tf, sealer_trusted_stk)); } +/* + * XXX Dapeng: These functions are kept here for compatibility with old libc and + * 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); + *buf = dl_c18n_get_trusted_stack(0); return (ret); } @@ -904,7 +920,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); + *buf = dl_c18n_get_trusted_stack(0); return (ret); } @@ -914,20 +930,14 @@ _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_stack(void *rcsp, void *target) { /* - * This helper is used by functions like longjmp. Before longjmp is - * called, the top of the trusted stack contains: - * 0. Link to previous frame - * longjmp does not push to the trusted stack. When _rtld_longjmp is - * called, the following are pushed to the trusted stack: - * 1. Caller's data - * 2. Link to 0 - * _rtld_longjmp traverses down the trusted stack from 0 and unwinds - * the stack of each intermediate compartment until reaching the target - * frame. + * Traverse the trusted stack and unwind the untrusted stack of each + * compartment until reaching the target compartment. Then return to the + * target compartment while installing the untrusted stack passed by the + * argument. */ void **ospp; @@ -937,6 +947,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. */ @@ -944,6 +957,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) { @@ -992,36 +1006,68 @@ 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_trampoline(uintptr_t pc, void *tfs) +{ + struct trusted_frame *tf; + + if (!C18N_ENABLED) + return (0); + + tf = cheri_unseal(tfs, sealer_trusted_stk); + if (!cheri_gettag(tf)) + return (0); + + return (c18n_is_tramp(pc, tf)); } +void * +dl_c18n_pop_trusted_stack(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 Dapeng: These functions are kept here for compatibility with old libc and + * 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_stack(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_stack(rcsp, *buf); + return (ret); } uintptr_t _rtld_unw_getsealer(void); uintptr_t _rtld_unw_getsealer(void) { - return (sealer_unwbuf); + return (sealer_trusted_stk); } /* @@ -1211,9 +1257,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)); @@ -1640,10 +1686,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); @@ -1988,7 +2031,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, @@ -2043,7 +2088,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..6a4c5bf3829a 100644 --- a/libexec/rtld-elf/rtld_c18n.h +++ b/libexec/rtld-elf/rtld_c18n.h @@ -28,6 +28,8 @@ #ifndef RTLD_C18N_H #define RTLD_C18N_H +#include + #include /* @@ -120,6 +122,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); @@ -146,6 +190,11 @@ pop_dummy_rtld_trusted_frame(struct trusted_frame *tf) return (tf); } +/* + * Stack unwinding + */ +int c18n_is_tramp(uintptr_t, const struct trusted_frame *); + /* * Trampolines */ @@ -217,8 +266,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..031fbeafeaab 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_stack + dl_c18n_unwind_trusted_stack + callee [RTLD] export to [libunwind] _rtld_unw_getcontext _rtld_unw_setcontext _rtld_unw_getsealer + dl_c18n_is_trampoline + dl_c18n_pop_trusted_stack diff --git a/sys/arm64/include/c18n.h b/sys/arm64/include/c18n.h new file mode 100644 index 000000000000..f030095e53d8 --- /dev/null +++ b/sys/arm64/include/c18n.h @@ -0,0 +1,45 @@ +/*- + * 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 __MACHINE_C18N_H__ +#define __MACHINE_C18N_H__ + +struct dl_c18n_compart_state { + void *fp; + void *pc; + /* + * c19 to c28 + */ + void *regs[10]; + /* + * INVARIANT: This field contains the top of the caller's stack when the + * caller made the call. + */ + void *sp; +}; + +#endif /* __MACHINE_C18N_H__ */ diff --git a/sys/sys/link_elf.h b/sys/sys/link_elf.h index f999a296d945..08125da5f7cf 100644 --- a/sys/sys/link_elf.h +++ b/sys/sys/link_elf.h @@ -110,6 +110,14 @@ int _rtld_addr_phdr(const void *, struct dl_phdr_info *); int _rtld_get_stack_prot(void); int _rtld_is_dlopened(void *); +#if defined(__CHERI_PURE_CAPABILITY__) && defined(__aarch64__) +#include +void *dl_c18n_get_trusted_stack(uintptr_t); +void dl_c18n_unwind_trusted_stack(void *, void *); +int dl_c18n_is_trampoline(uintptr_t, void *); +void *dl_c18n_pop_trusted_stack(struct dl_c18n_compart_state *, void *); +#endif + #ifdef __ARM_EABI__ void * dl_unwind_find_exidx(const void *, int *); #endif