Skip to content

Commit

Permalink
rtld: Use compartment IDs from sub-object compartments for policy enf…
Browse files Browse the repository at this point in the history
…orcement

- Add a helper function to lookup the relevant compartment ID for a given
  virtual address and shared object.

- Save a compartment ID for each PLT (based on the address of the
  associated PLT GOT) and use this as the "subject" (caller) for
  policy enforcement when handling PLT GOT relocations.

- Use the target address of a function call to determine the "object"
  (callee).

Note: rtld currently does not enforce any policy for access to data
via the normal GOT.
  • Loading branch information
bsdjhb committed Jan 7, 2025
1 parent 51db786 commit f179912
Show file tree
Hide file tree
Showing 8 changed files with 81 additions and 30 deletions.
11 changes: 8 additions & 3 deletions libexec/rtld-elf/aarch64/reloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,7 @@ reloc_plt(Plt_Entry *plt, int flags, RtldLockState *lockstate)
defobj);
#ifdef CHERI_LIB_C18N
target = (uintptr_t)tramp_intern(obj,
plt->compart_id,
&(struct tramp_data) {
.target = (void *)target,
.defobj = defobj,
Expand Down Expand Up @@ -571,7 +572,8 @@ reloc_jmpslots(Plt_Entry *plt, int flags, RtldLockState *lockstate)
}
target = (uintptr_t)make_function_pointer(def, defobj);
#ifdef CHERI_LIB_C18N
target = (uintptr_t)tramp_intern(obj, &(struct tramp_data) {
target = (uintptr_t)tramp_intern(obj, plt->compart_id,
&(struct tramp_data) {
.target = (void *)target,
.defobj = defobj,
.def = def,
Expand Down Expand Up @@ -632,7 +634,8 @@ reloc_iresolve_one(Obj_Entry *obj, const Elf_Rela *rela,
#endif
lock_release(rtld_bind_lock, lockstate);
#ifdef CHERI_LIB_C18N
ptr = (uintptr_t)tramp_intern(NULL, &(struct tramp_data) {
ptr = (uintptr_t)tramp_intern(NULL, RTLD_COMPART_ID,
&(struct tramp_data) {
.target = (void *)ptr,
.defobj = obj,
.sig = (struct func_sig) { .valid = true,
Expand Down Expand Up @@ -729,7 +732,9 @@ reloc_gnu_ifunc_plt(Plt_Entry *plt, int flags, RtldLockState *lockstate)
lock_release(rtld_bind_lock, lockstate);
target = (uintptr_t)rtld_resolve_ifunc(defobj, def);
#ifdef CHERI_LIB_C18N
target = (uintptr_t)tramp_intern(obj, &(struct tramp_data) {
target = (uintptr_t)tramp_intern(obj,
plt->compart_id,
&(struct tramp_data) {
.target = (void *)target,
.defobj = defobj,
.def = def,
Expand Down
2 changes: 1 addition & 1 deletion libexec/rtld-elf/aarch64/rtld_c18n_machdep.c
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ _rtld_sandbox_code(void *target, struct func_sig sig)
target = cheri_sealentry(target_unsealed);
}

target = tramp_intern(NULL, &(struct tramp_data) {
target = tramp_intern(NULL, RTLD_COMPART_ID, &(struct tramp_data) {
.target = target,
.defobj = obj,
.sig = sig
Expand Down
6 changes: 4 additions & 2 deletions libexec/rtld-elf/aarch64/rtld_machdep.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,15 +86,17 @@ 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) { \
(((InitArrFunc)tramp_intern(NULL, RTLD_COMPART_ID, \
&(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))

#define call_fini_array_pointer(_obj, _target) \
(((InitFunc)tramp_intern(NULL, &(struct tramp_data) { \
(((InitFunc)tramp_intern(NULL, RTLD_COMPART_ID, \
&(struct tramp_data) { \
.target = (void *)(_target).value, \
.defobj = _obj, \
.sig = (struct func_sig) { .valid = true, \
Expand Down
46 changes: 37 additions & 9 deletions libexec/rtld-elf/rtld.c
Original file line number Diff line number Diff line change
Expand Up @@ -1151,7 +1151,8 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp)
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) {
rtld_exit_ptr = tramp_intern(NULL, RTLD_COMPART_ID,
&(struct tramp_data) {
.target = rtld_exit_ptr,
.defobj = &obj_rtld,
.sig = (struct func_sig) {
Expand All @@ -1165,7 +1166,8 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp)
*objp = obj_main;

#ifdef CHERI_LIB_C18N
return ((func_ptr_type)tramp_intern(NULL, &(struct tramp_data) {
return ((func_ptr_type)tramp_intern(NULL, RTLD_COMPART_ID,
&(struct tramp_data) {
.target = cheri_sealentry(obj_main->entry),
.defobj = obj_main,
.sig = (struct func_sig) {
Expand All @@ -1186,7 +1188,7 @@ rtld_resolve_ifunc(const Obj_Entry *obj, const Elf_Sym *def)

ptr = (void *)make_function_pointer(def, obj);
#ifdef CHERI_LIB_C18N
ptr = tramp_intern(NULL, &(struct tramp_data) {
ptr = tramp_intern(NULL, RTLD_COMPART_ID, &(struct tramp_data) {
.target = ptr,
.defobj = obj,
.def = def,
Expand Down Expand Up @@ -1237,7 +1239,8 @@ _rtld_bind(Plt_Entry *plt, Elf_Size reloff)
#ifdef __CHERI_PURE_CAPABILITY__
target = (uintptr_t)make_function_pointer(def, defobj);
#ifdef CHERI_LIB_C18N
target = (uintptr_t)tramp_intern(obj, &(struct tramp_data) {
target = (uintptr_t)tramp_intern(obj, plt->compart_id,
&(struct tramp_data) {
.target = (void *)target,
.defobj = defobj,
.def = def,
Expand Down Expand Up @@ -3638,7 +3641,8 @@ objlist_call_init(Objlist *list, RtldLockState *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) {
exit_ptr = tramp_intern(NULL, RTLD_COMPART_ID,
&(struct tramp_data) {
.target = exit_ptr,
.defobj = &obj_rtld,
.sig = (struct func_sig) {
Expand All @@ -3651,7 +3655,8 @@ objlist_call_init(Objlist *list, RtldLockState *lockstate)
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) {
rtld_exit_ptr = tramp_intern(NULL, RTLD_COMPART_ID,
&(struct tramp_data) {
.target = rtld_exit_ptr,
.defobj = &obj_rtld,
.sig = (struct func_sig) {
Expand Down Expand Up @@ -4848,7 +4853,7 @@ dl_iterate_phdr(__dl_iterate_hdr_callback callback, void *param)
error = 0;

#ifdef CHERI_LIB_C18N
callback = tramp_intern(NULL, &(struct tramp_data) {
callback = tramp_intern(NULL, RTLD_COMPART_ID, &(struct tramp_data) {
.target = callback,
.defobj = obj_from_addr(callback),
.sig = (struct func_sig) {
Expand Down Expand Up @@ -5159,7 +5164,7 @@ get_program_var_addr(const char *name, RtldLockState *lockstate)
if (ELF_ST_TYPE(req.sym_out->st_info) == STT_FUNC) {
void *target = make_function_pointer(req.sym_out, req.defobj_out);
#ifdef CHERI_LIB_C18N
target = tramp_intern(NULL, &(struct tramp_data) {
target = tramp_intern(NULL, RTLD_COMPART_ID, &(struct tramp_data) {
.target = target,
.defobj = req.defobj_out,
.def = req.sym_out
Expand All @@ -5169,7 +5174,7 @@ get_program_var_addr(const char *name, RtldLockState *lockstate)
} else if (ELF_ST_TYPE(req.sym_out->st_info) == STT_GNU_IFUNC) {
void *target = rtld_resolve_ifunc(req.defobj_out, req.sym_out);
#ifdef CHERI_LIB_C18N
target = tramp_intern(NULL, &(struct tramp_data) {
target = tramp_intern(NULL, RTLD_COMPART_ID, &(struct tramp_data) {
.target = target,
.defobj = req.defobj_out,
.def = req.sym_out
Expand Down Expand Up @@ -6376,6 +6381,28 @@ c18n_setup_compartments(Obj_Entry *obj, const char *name)
}
}

compart_id_t
compart_id_for_address(const Obj_Entry *obj, Elf_Addr addr)
{
assert(cheri_is_address_inbounds(obj->relocbase, addr));

for (unsigned long i = 0; i < obj->ncomparts; i++) {
if (addr >= obj->comparts[i].start &&
addr < obj->comparts[i].end)
return (obj->comparts[i].compart_id);
}
return (obj->compart_id);
}

static void
c18n_assign_plt_compartments(Obj_Entry *obj)
{
for (unsigned long i = 0; i < obj->nplts; i++) {
obj->plts[i].compart_id = compart_id_for_address(obj,
(ptraddr_t)obj->plts[i].pltgot);
}
}

static bool
c18n_add_obj(Obj_Entry *obj, const char *name)
{
Expand All @@ -6400,6 +6427,7 @@ c18n_add_obj(Obj_Entry *obj, const char *name)
}

c18n_setup_compartments(obj, name);
c18n_assign_plt_compartments(obj);
return (true);
}
#endif
Expand Down
3 changes: 3 additions & 0 deletions libexec/rtld-elf/rtld.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,9 @@ typedef struct Struct_Plt_Entry {
const Elf_Rela *rela; /* PLT relocation entries with addend */
unsigned long relasize; /* Size in bytes of PLT addend reloc info */
bool jmpslots_done : 1; /* Already have relocated the jump slots */

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

View workflow job for this annotation

GitHub Actions / Style Checker

spaces prohibited around that ':' (ctx:WxW)
#ifdef CHERI_LIB_C18N
uint16_t compart_id;
#endif
MD_PLT_ENTRY;
} Plt_Entry;

Expand Down
38 changes: 25 additions & 13 deletions libexec/rtld-elf/rtld_c18n.c
Original file line number Diff line number Diff line change
Expand Up @@ -546,8 +546,10 @@ evaluate_rules(compart_id_t caller, compart_id_t callee, const char *sym)
}

static bool
tramp_should_include(const Obj_Entry *reqobj, const struct tramp_data *data)
tramp_should_include(const Obj_Entry *reqobj, compart_id_t caller,
const struct tramp_data *data)
{
compart_id_t callee;
const char *sym;

if (data->def == NULL)
Expand All @@ -561,23 +563,28 @@ 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)
if (reqobj == NULL) {
assert(caller == RTLD_COMPART_ID);
return (true);
}

if (reqobj->compart_id == data->defobj->compart_id)
callee = compart_id_for_address(data->defobj,
(ptraddr_t)data->defobj->relocbase + data->def->st_value);

if (caller == callee)
return (false);

if (string_base_search(&comparts.data[reqobj->compart_id].trusts, sym)
if (string_base_search(&comparts.data[caller].trusts, sym)
!= -1)
return (false);

if (evaluate_rules(reqobj->compart_id, data->defobj->compart_id, sym))
if (evaluate_rules(caller, callee, sym))
return (true);

rtld_fatal("c18n: Policy violation: %s is not allowed to access symbol "
"%s defined by %s",
comparts.data[reqobj->compart_id].name, sym,
comparts.data[data->defobj->compart_id].name);
comparts.data[caller].name, sym,
comparts.data[callee].name);
}

/*
Expand Down Expand Up @@ -1375,7 +1382,8 @@ tramp_make_entry(const struct tramp_header *header)
}

void *
tramp_intern(const Obj_Entry *reqobj, const struct tramp_data *data)
tramp_intern(const Obj_Entry *reqobj, compart_id_t caller,
const struct tramp_data *data)
{
RtldLockState lockstate;
const struct tramp_header *header;
Expand Down Expand Up @@ -1411,7 +1419,7 @@ tramp_intern(const Obj_Entry *reqobj, const struct tramp_data *data)
data->defobj->dynsymcount);
assert(func_sig_legal(data->sig));

if (!tramp_should_include(reqobj, data))
if (!tramp_should_include(reqobj, caller, data))
return (data->target);

start:
Expand Down Expand Up @@ -1720,7 +1728,8 @@ c18n_init2(Obj_Entry *obj_rtld)
* XXX: Manually wrap _rtld_unw_setcontext_impl in a trampoline for now
* because it is called via a function pointer.
*/
_rtld_unw_setcontext_ptr = tramp_intern(NULL, &(struct tramp_data) {
_rtld_unw_setcontext_ptr = tramp_intern(NULL, RTLD_COMPART_ID,
&(struct tramp_data) {
.target = &_rtld_unw_setcontext_impl,
.defobj = obj_rtld,
.sig = (struct func_sig) {
Expand All @@ -1744,7 +1753,8 @@ _rtld_thread_start_init(void (*p)(struct pthread *))
{
assert((cheri_getperm(p) & CHERI_PERM_EXECUTIVE) == 0);
assert(thr_thread_start == NULL);
thr_thread_start = tramp_intern(NULL, &(struct tramp_data) {
thr_thread_start = tramp_intern(NULL, RTLD_COMPART_ID,
&(struct tramp_data) {
.target = p,
.defobj = obj_from_addr(p),
.sig = (struct func_sig) {
Expand Down Expand Up @@ -1888,7 +1898,8 @@ _rtld_sighandler_init(__siginfohandler_t *handler)
{
assert((cheri_getperm(handler) & CHERI_PERM_EXECUTIVE) == 0);
assert(signal_dispatcher == sigdispatch);
signal_dispatcher = tramp_intern(NULL, &(struct tramp_data) {
signal_dispatcher = tramp_intern(NULL, RTLD_COMPART_ID,
&(struct tramp_data) {
.target = handler,
.defobj = obj_from_addr(handler),
.sig = (struct func_sig) {
Expand Down Expand Up @@ -2126,7 +2137,8 @@ _rtld_siginvoke(int sig, siginfo_t *info, ucontext_t *ucp,
header = tramp_reflect(sigfunc);
if (header == NULL) {
defobj = obj_from_addr(sigfunc);
sigfunc = tramp_intern(NULL, &(struct tramp_data) {
sigfunc = tramp_intern(NULL, RTLD_COMPART_ID,
&(struct tramp_data) {
.target = sigfunc,
.defobj = defobj
});
Expand Down
3 changes: 2 additions & 1 deletion libexec/rtld-elf/rtld_c18n.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ typedef uint16_t compart_id_t;
typedef struct { uint16_t val; } stk_table_index;

compart_id_t compart_id_allocate(const char *);
compart_id_t compart_id_for_address(const Obj_Entry *, Elf_Addr);

/*
* Stack switching
Expand Down Expand Up @@ -244,7 +245,7 @@ void tramp_hook(void);

size_t tramp_compile(char **, const struct tramp_data *);

void *tramp_intern(const Obj_Entry *reqobj, const struct tramp_data *);
void *tramp_intern(const Obj_Entry *, compart_id_t, const struct tramp_data *);
struct tramp_header *tramp_reflect(const void *);
struct func_sig sigtab_get(const Obj_Entry *, unsigned long);

Expand Down
2 changes: 1 addition & 1 deletion libexec/rtld-elf/rtld_lock.c
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,7 @@ _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 = tramp_intern(NULL, RTLD_COMPART_ID, &(struct tramp_data) { \

Check warning on line 431 in libexec/rtld-elf/rtld_lock.c

View workflow job for this annotation

GitHub Actions / Style Checker

line over 80 characters
.target = _target, \
.defobj = obj, \
.sig = (struct func_sig) { \
Expand Down

0 comments on commit f179912

Please sign in to comment.