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.

Co-authored-by: Dapeng Gao <[email protected]>
  • Loading branch information
bsdjhb and dpgao committed Jan 29, 2025
1 parent 58e4e87 commit ac754ad
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 47 deletions.
12 changes: 8 additions & 4 deletions libexec/rtld-elf/aarch64/reloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,8 @@ reloc_plt(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,
target = (uintptr_t)tramp_intern(plt,
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(plt, 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,8 @@ 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(plt, plt->compart_id,
&(struct tramp_data) {
.target = (void *)target,
.defobj = defobj,
.def = def,
Expand Down
6 changes: 4 additions & 2 deletions libexec/rtld-elf/aarch64/rtld_c18n_machdep.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ tramp_compile(char **entry, const struct tramp_data *data)
char *buf = *entry;
size_t hook_off, count_off;
size_t header_off, target_off, landing_off, unused_regs;
compart_id_t callee;
bool executive = cheri_getperm(data->target) & CHERI_PERM_EXECUTIVE;
bool count = ld_compartment_switch_count != NULL;
bool hook = ld_compartment_utrace != NULL ||
Expand Down Expand Up @@ -143,9 +144,10 @@ tramp_compile(char **entry, const struct tramp_data *data)
target_off = size + offsetof(struct tramp_header, target);
*entry = buf + size;
size += offsetof(struct tramp_header, entry);
callee = compart_id_for_address(data->defobj, (ptraddr_t)data->target);

COPY(push_frame);
PATCH_MOV(push_frame, cid, cid_to_index(data->defobj->compart_id).val);
PATCH_MOV(push_frame, cid, cid_to_index(callee).val);
landing_off = PATCH_OFF(push_frame, landing);
/*
* The trampoline computes the number of return value registers and
Expand Down Expand Up @@ -299,7 +301,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
50 changes: 39 additions & 11 deletions libexec/rtld-elf/rtld.c
Original file line number Diff line number Diff line change
Expand Up @@ -1152,7 +1152,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 @@ -1166,7 +1167,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 @@ -1187,7 +1189,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 @@ -1238,7 +1240,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(plt, plt->compart_id,
&(struct tramp_data) {
.target = (void *)target,
.defobj = defobj,
.def = def,
Expand Down Expand Up @@ -3644,7 +3647,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 @@ -3657,7 +3661,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 @@ -4830,7 +4835,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 @@ -5141,7 +5146,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 @@ -5151,7 +5156,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 @@ -6319,8 +6324,8 @@ c18n_setup_compartments(Obj_Entry *obj, const char *name)
const Elf_Phdr *ph;
size_t len;

assert(obj->compart_id == 0);
obj->compart_id = compart_id_allocate(name);
assert(obj->default_compart_id == 0);
obj->default_compart_id = compart_id_allocate(name);

for (ph = obj->phdr; (const char *)ph < (const char *)obj->phdr +
obj->phsize; ph++) {
Expand Down Expand Up @@ -6358,6 +6363,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->default_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 @@ -6382,6 +6409,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
5 changes: 4 additions & 1 deletion libexec/rtld-elf/rtld.h
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,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 */
#ifdef CHERI_LIB_C18N
uint16_t compart_id;
#endif
MD_PLT_ENTRY;
} Plt_Entry;

Expand Down Expand Up @@ -320,7 +323,7 @@ typedef struct Struct_Obj_Entry {

#ifdef CHERI_LIB_C18N
const char *soname;
uint16_t compart_id;
uint16_t default_compart_id;
const struct func_sig *sigtab;
#endif

Expand Down
59 changes: 34 additions & 25 deletions libexec/rtld-elf/rtld_c18n.c
Original file line number Diff line number Diff line change
Expand Up @@ -561,8 +561,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 Plt_Entry *plt, compart_id_t caller,
const struct tramp_data *data)
{
compart_id_t callee;
const char *sym;

/* XXX: This not be needed once function pointers are wrapped. */
Expand All @@ -580,23 +582,26 @@ 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)
return (true);
callee = compart_id_for_address(data->defobj, (ptraddr_t)data->target);

if (reqobj->compart_id == data->defobj->compart_id)
/*
* Jump slots within the same compartment do not require a
* trampoline.
*/
if (plt != NULL && 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 @@ -1363,7 +1368,8 @@ tramp_check_sig(const struct tramp_header *found, const Obj_Entry *reqobj,
"%s requests " C18N_SIG_FORMAT_STRING " but "
"%s provides " C18N_SIG_FORMAT_STRING,
symname(found->defobj, found->symnum),
reqobj->path, C18N_SIG_FORMAT(data->sig),
reqobj != NULL ? reqobj->path : "",
C18N_SIG_FORMAT(data->sig),
found->defobj->path, C18N_SIG_FORMAT(sig));
}
}
Expand Down Expand Up @@ -1394,7 +1400,8 @@ tramp_make_entry(const struct tramp_header *header)
}

void *
tramp_intern(const Obj_Entry *reqobj, const struct tramp_data *data)
tramp_intern(const Plt_Entry *plt, compart_id_t caller,
const struct tramp_data *data)
{
RtldLockState lockstate;
const struct tramp_header *header;
Expand All @@ -1413,15 +1420,11 @@ tramp_intern(const Obj_Entry *reqobj, const struct tramp_data *data)
assert(cheri_gettag(data->defobj));
if (data->def == NULL)
/*
* XXX-DG: reqobj != NULL causes policies to be evaluated which
* might result in a trampoline being elided. This is only safe
* to do for jump slot relocations.
*
* Currently, the decision to elide the trampoline or not is
* coupled with the decision of whether the symbol should be
* made accesible to the requesting object. This is insecure.
* made accessible to the requesting object. This is insecure.
*/
assert(reqobj == NULL);
assert(plt == NULL);
else if (data->def == &sym_zero)
assert(data->target == NULL);
else
Expand All @@ -1430,7 +1433,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(plt, caller, data))
return (data->target);

start:
Expand Down Expand Up @@ -1531,7 +1534,7 @@ tramp_intern(const Obj_Entry *reqobj, const struct tramp_data *data)
end:
lock_release(rtld_tramp_lock, &lockstate);

tramp_check_sig(header, reqobj, data);
tramp_check_sig(header, plt != NULL ? plt->obj : NULL, data);

/*
* Most consumers use type (void *) for function pointers.
Expand Down Expand Up @@ -1739,7 +1742,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 @@ -1763,7 +1767,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 @@ -1907,7 +1912,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 @@ -2145,13 +2151,16 @@ _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) {
callee = compart_id_for_address(defobj, (ptraddr_t)sigfunc);
sigfunc = tramp_intern(NULL, RTLD_COMPART_ID,
&(struct tramp_data) {
.target = sigfunc,
.defobj = defobj
});
} else
defobj = header->defobj;
callee = defobj->compart_id;
} else {
callee = compart_id_for_address(header->defobj,
(ptraddr_t)header->target);
}
callee_idx = cid_to_index(callee);

/*
Expand Down
Loading

0 comments on commit ac754ad

Please sign in to comment.