Skip to content

Commit

Permalink
c18n: Expose new unwinding APIs
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
dpgao committed Aug 22, 2024
1 parent 1782133 commit 3b76a6e
Show file tree
Hide file tree
Showing 8 changed files with 111 additions and 107 deletions.
4 changes: 2 additions & 2 deletions lib/libc/aarch64/gen/_setjmp.S
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ ENTRY(_setjmp)
/*
* Tail-call to save Executive mode state
*/
b _rtld_setjmp
b c18n_get_trusted_stk
#else
RETURN
#endif
Expand Down Expand Up @@ -112,7 +112,7 @@ ENTRY(_longjmp)
/*
* Tail-call to restore Executive mode state
*/
b _rtld_longjmp
b c18n_unwind_trusted_stk
#else
RETURN
#endif
Expand Down
9 changes: 2 additions & 7 deletions lib/libc/aarch64/gen/setjmp.S
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,6 @@
#include <machine/setjmp.h>
#include <sys/elf_common.h>

#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)]
Expand Down Expand Up @@ -78,7 +73,7 @@ ENTRY(setjmp)
/*
* Tail-call to save Executive mode state
*/
b _rtld_setjmp
b c18n_get_trusted_stk
#else
RETURN
#endif
Expand Down Expand Up @@ -139,7 +134,7 @@ ENTRY(longjmp)
/*
* Tail-call to restore Executive mode state
*/
b _rtld_longjmp
b c18n_unwind_trusted_stk
#else
RETURN
#endif
Expand Down
8 changes: 5 additions & 3 deletions lib/libgcc_s/Symbol-c18n.map
Original file line number Diff line number Diff line change
@@ -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;
};
10 changes: 5 additions & 5 deletions libexec/rtld-elf/Symbol-c18n.map
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
34 changes: 1 addition & 33 deletions libexec/rtld-elf/aarch64/rtld_c18n_machdep.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ set_untrusted_stk(const void *sp)
}
#endif

struct trusted_frame {
struct compart_state {
void *fp;
void *pc;
/*
Expand All @@ -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
93 changes: 43 additions & 50 deletions libexec/rtld-elf/rtld_c18n.c
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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 **);

Check failure on line 864 in libexec/rtld-elf/rtld_c18n.c

View workflow job for this annotation

GitHub Actions / Style Checker

externs should be avoided in .c files
uintptr_t c18n_unwind_trusted_stk(uintptr_t, void *, struct trusted_frame *);

Check failure on line 865 in libexec/rtld-elf/rtld_c18n.c

View workflow job for this annotation

GitHub Actions / Style Checker

externs should be avoided in .c files

bool c18n_is_enabled(void);

Check failure on line 867 in libexec/rtld-elf/rtld_c18n.c

View workflow job for this annotation

GitHub Actions / Style Checker

externs should be avoided in .c files
bool c18n_is_tramp(ptraddr_t, struct trusted_frame *);

Check failure on line 868 in libexec/rtld-elf/rtld_c18n.c

View workflow job for this annotation

GitHub Actions / Style Checker

externs should be avoided in .c files
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
Expand All @@ -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
Expand All @@ -920,13 +913,17 @@ 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.
*/
SIGFILLSET(nset);
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,
Expand Down Expand Up @@ -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;
Expand All @@ -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));
}

/*
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
}

Expand Down
46 changes: 44 additions & 2 deletions libexec/rtld-elf/rtld_c18n.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Check failure on line 155 in libexec/rtld-elf/rtld_c18n.h

View workflow job for this annotation

GitHub Actions / Style Checker

spaces prohibited around that ':' (ctx:WxW)
uint16_t reserved : 14;

Check failure on line 156 in libexec/rtld-elf/rtld_c18n.h

View workflow job for this annotation

GitHub Actions / Style Checker

spaces prohibited around that ':' (ctx:WxW)
/*
* 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);

Expand Down Expand Up @@ -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);
Expand Down
14 changes: 9 additions & 5 deletions libexec/rtld-elf/rtld_c18n_policy.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit 3b76a6e

Please sign in to comment.