Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Userspace runtime loader support for sub-library c18n #2267

Open
wants to merge 13 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions libexec/rtld-elf/aarch64/reloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@
*/
static uintcap_t
init_cap_from_fragment(const Elf_Addr *fragment, void * __capability data_cap,
const void * __capability pcc_cap, Elf_Addr base_addr,

Check failure on line 150 in libexec/rtld-elf/aarch64/reloc.c

View workflow job for this annotation

GitHub Actions / Style Checker

"foo * bar" should be "foo *bar"
Elf_Size addend)
{
uintcap_t cap;
Expand Down Expand Up @@ -493,6 +493,7 @@
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 @@
}
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 @@
#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 @@
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 Expand Up @@ -799,7 +804,7 @@
Elf_Addr *where, symval;
#if __has_feature(capabilities)
void * __capability data_cap;
const void * __capability code_cap;

Check failure on line 807 in libexec/rtld-elf/aarch64/reloc.c

View workflow job for this annotation

GitHub Actions / Style Checker

"foo * bar" should be "foo *bar"
#endif

#ifdef __CHERI_PURE_CAPABILITY__
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 @@ -284,7 +284,7 @@
#else
func_ptr_type _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp);
#endif
uintptr_t _rtld_bind(Plt_Entry *obj, Elf_Size reloff);

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

View workflow job for this annotation

GitHub Actions / Style Checker

externs should be avoided in .c files

int npagesizes;
static int osreldate;
Expand Down Expand Up @@ -1151,7 +1151,8 @@
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 @@
*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 @@

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 @@
#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 @@ -1483,12 +1486,12 @@
pcc_cap = cheri_setbounds(pcc_cap, ph->p_memsz);
if (cheri_getbase(pcc_cap) !=
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a TODO that in future this should be simplified to cheri_setbounds_exact + cheri_gettag, once we can assume tag clearing on RISC-V (which is almost there...)?

cheri_getaddress(pcc_cap)) {
_rtld_error("pcc_cap %#p start is not aligned for %s",

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

View workflow job for this annotation

GitHub Actions / Style Checker

line over 80 characters
pcc_cap, obj->path);
return (false);
}
if (cheri_getlen(pcc_cap) != ph->p_memsz) {
_rtld_error("pcc_cap %#p length is not %zu for %s",

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

View workflow job for this annotation

GitHub Actions / Style Checker

line over 80 characters
pcc_cap, (size_t)ph->p_memsz, obj->path);
return (false);
}
Expand All @@ -1513,7 +1516,7 @@
cheri_getbase(pcc_cap)) ||
cheri_is_address_inbounds(obj->pcc_caps[j],
cheri_gettop(pcc_cap))) {
_rtld_error("Overlapping PCC capabilities for %s",

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

View workflow job for this annotation

GitHub Actions / Style Checker

line over 80 characters
obj->path);
return (false);
}
Expand Down Expand Up @@ -3638,7 +3641,8 @@
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 @@
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 @@
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 @@
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 @@
} 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 @@
}
}

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_setup_compartments(obj, name);
c18n_assign_plt_compartments(obj);
return (true);
}
#endif
Expand Down Expand Up @@ -6695,7 +6723,7 @@
dbg("Enforcing RELRO for %s (%p -> %p)", obj->path,
relro_page, relro_page + relro_size);
if (mprotect(relro_page, relro_size, prot) == -1) {
_rtld_error("%s: Cannot set relro protection to %#x: %s",

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

View workflow job for this annotation

GitHub Actions / Style Checker

line over 80 characters
obj->path, prot, rtld_strerror(errno));
return (-1);
}
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 @@ -173,7 +173,10 @@
unsigned long relsize; /* Size in bytes of PLT relocation info */
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,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just pass the Plt_Entry instead of the Obj_Entry and use reqplt->compart_id everywhere?

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 @@
#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
Loading