From dacb4af5bcc3180d785bc2bb924166c86946e7e7 Mon Sep 17 00:00:00 2001 From: Andrei Lascu Date: Mon, 3 Jun 2024 12:25:09 +0100 Subject: [PATCH] Add TLS support Initial support for `_Thread_local` (and `_Thread_local static`) variables. Limitations: * Supports a single TLS region, but amenable to future extensions if we want to implement multi-threaded compartments; * Handles only symbols of type `TLSDESC`. Other changes: * Remove some old `struct Compartment` member variables that are now unneeded; * Add `print_comp` back; * Fix a bug regarding calculating scratch memory size for compartments; * Fixed a bug with setting relocation target addresses. --- include/comp_utils.h | 1 + include/compartment.h | 43 +-- include/manager.h | 6 - src/comp_utils.c | 31 +- src/compartment.c | 448 +++++++++++++++++++++++------ src/manager.c | 28 +- src/transition.S | 29 +- tests/CMakeLists.txt | 48 ++-- tests/simple_thrloc_var-external.c | 33 +++ tests/simple_thrloc_var.c | 46 +++ tests/{time.c => simple_time.c} | 1 - 11 files changed, 556 insertions(+), 158 deletions(-) create mode 100644 tests/simple_thrloc_var-external.c create mode 100644 tests/simple_thrloc_var.c rename tests/{time.c => simple_time.c} (81%) diff --git a/include/comp_utils.h b/include/comp_utils.h index c230ae7..d5637d2 100644 --- a/include/comp_utils.h +++ b/include/comp_utils.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "cheriintrin.h" diff --git a/include/compartment.h b/include/compartment.h index f84e1a8..f94466e 100644 --- a/include/compartment.h +++ b/include/compartment.h @@ -34,11 +34,9 @@ // currently there's quite a bit of redundancy to make things easier to think // about -void -compartment_transition_out(); int64_t -comp_exec_in( - void *, void *__capability, void *, void *, size_t, void *__capability); +comp_exec_in(void *, void *__capability, void *, void *, size_t, + void *__capability, void *); void comp_exec_out(); @@ -48,8 +46,6 @@ extern void __clear_cache(void *, void *); // Number of instructions required by the transition function -#define COMP_TRANS_FN_INSTR_CNT 4 - extern void *__capability sealed_redirect_cap; extern void *__capability comp_return_caps[2]; @@ -145,6 +141,26 @@ struct LibDependency // Symbols within this library that need eager relocation size_t rela_maps_count; struct LibRelaMapping *rela_maps; + + // TLS-related variables + // TODO can there be more TLS sections? + void *tls_sec_addr; + size_t tls_sec_size; + size_t tls_data_size; +}; + +/** + * Struct representing TLS information for a compartment. Since we currently + * enforce only single-threaded code, we basically only have pointers for + * regions allocated for TLS for each dynamic shared object + */ +struct TLSDesc +{ + unsigned short region_count; + size_t region_size; + void *region_start; + unsigned short libs_count; + unsigned short *lib_idxs; }; /** @@ -166,29 +182,18 @@ struct Compartment // Scratch memory void *scratch_mem_base; size_t scratch_mem_size; - size_t scratch_mem_alloc; size_t scratch_mem_heap_size; void *scratch_mem_stack_top; size_t scratch_mem_stack_size; - void *stack_pointer; - struct MemAlloc *alloc_head; - - // TODO double check / rework this process - void *manager_caps; - size_t max_manager_caps_count; - size_t active_manager_caps_count; - - // Transition function (duplicated across compartments, but must be within - // to be within DDC bounds) - void *mng_trans_fn; - size_t mng_trans_fn_sz; // Internal libraries and relocations size_t libs_count; struct LibDependency **libs; size_t entry_point_count; struct CompEntryPoint *entry_points; + void *tls_lookup_func; + struct TLSDesc *libs_tls_sects; // Hardware info - maybe move size_t page_size; diff --git a/include/manager.h b/include/manager.h index d00b182..01dfafb 100644 --- a/include/manager.h +++ b/include/manager.h @@ -24,12 +24,6 @@ extern void *__capability manager_ddc; extern struct CompWithEntries **comps; extern struct Compartment *loaded_comp; -/******************************************************************************* - * Utility Functions - ******************************************************************************/ - -void print_full_cap(uintcap_t); - /******************************************************************************* * Compartment ******************************************************************************/ diff --git a/src/comp_utils.c b/src/comp_utils.c index 259f247..8e06715 100644 --- a/src/comp_utils.c +++ b/src/comp_utils.c @@ -3,15 +3,33 @@ static void *malloc_ptr; static size_t heap_mem_left; +#define NON_COMP_DEFAULT_SIZE (10 * 1024) // 10 MB + void * malloc(size_t to_alloc) { if (!malloc_ptr) { void *__capability ddc = cheri_ddc_get(); - malloc_ptr = (char *) cheri_address_get(ddc); - heap_mem_left = cheri_length_get(ddc) - cheri_offset_get(ddc); + if (cheri_base_get(ddc) == 0) + { + malloc_ptr = mmap(0, NON_COMP_DEFAULT_SIZE, PROT_WRITE | PROT_READ, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + heap_mem_left = NON_COMP_DEFAULT_SIZE; + if (malloc_ptr == MAP_FAILED) + { + err(1, "Failed `mmap`"); + } + } + else + { + malloc_ptr = (char *) cheri_address_get(ddc); + // TODO move heap to the end of the compartment; currently, it's at + // the beginning of the memory scratch area + heap_mem_left = cheri_length_get(ddc) - cheri_offset_get(ddc); + } } + if (to_alloc > heap_mem_left) { errx(1, "Insufficient heap space left."); @@ -44,3 +62,12 @@ realloc(void *to_realloc, size_t new_size) return malloc(new_size); } + +void +tls_lookup_stub() +{ + // Get TLS index + // TODO works only for one TLS region + asm("ldr x0, [x0, #8]" : :); + return; +} diff --git a/src/compartment.c b/src/compartment.c index 2257788..9ee41f6 100644 --- a/src/compartment.c +++ b/src/compartment.c @@ -1,6 +1,8 @@ #include "compartment.h" const char *libs_path_env_var = "COMP_LIBRARY_PATH"; +const char *tls_rtld_dropin = "tls_lookup_stub"; +const char *comp_utils_soname = "libcomputils.so"; /******************************************************************************* * Forward declarations @@ -31,11 +33,15 @@ static char * find_in_dir(const char *, char *); static void init_comp_scratch_mem(struct Compartment *); +static void +resolve_comp_tls_regions(struct Compartment *); static void print_lib_dep_seg(struct SegmentMap *); static void print_lib_dep(struct LibDependency *); +static void +print_comp(struct Compartment *); /******************************************************************************* * Main compartment functions @@ -60,26 +66,16 @@ comp_init() new_comp->scratch_mem_base = NULL; new_comp->scratch_mem_size = 0; - new_comp->scratch_mem_alloc = 0; new_comp->scratch_mem_heap_size = 0; new_comp->scratch_mem_stack_top = NULL; new_comp->scratch_mem_stack_size = 0; - new_comp->stack_pointer = NULL; - new_comp->alloc_head = NULL; - - new_comp->manager_caps = NULL; - new_comp->max_manager_caps_count = 0; - new_comp->active_manager_caps_count = 0; - - new_comp->mng_trans_fn = NULL; - new_comp->mng_trans_fn_sz - = sizeof(uint32_t) * COMP_TRANS_FN_INSTR_CNT; // TODO ptr arithmetic new_comp->libs_count = 0; new_comp->libs = NULL; new_comp->entry_point_count = 0; new_comp->entry_points = NULL; + new_comp->libs_tls_sects = NULL; new_comp->page_size = sysconf(_SC_PAGESIZE); @@ -119,6 +115,22 @@ comp_from_elf(char *filename, char **entry_points, size_t entry_point_count, struct LibDependency *parsed_lib = parse_lib_file(libs_to_parse[libs_parsed_count], new_comp); + // Get `tls_lookup_func` if we parsed `comp_utils.so` + if (!strcmp(parsed_lib->lib_name, comp_utils_soname)) + { + for (size_t i = 0; i < parsed_lib->lib_syms_count; ++i) + { + if (!strcmp(parsed_lib->lib_syms[i].sym_name, tls_rtld_dropin)) + { + new_comp->tls_lookup_func + = (char *) parsed_lib->lib_syms[i].sym_offset + + (intptr_t) parsed_lib->lib_mem_base; + break; + } + } + assert(new_comp->tls_lookup_func); + } + const unsigned short libs_to_search_count = libs_to_parse_count; for (size_t i = 0; i < parsed_lib->lib_dep_count; ++i) { @@ -145,10 +157,18 @@ comp_from_elf(char *filename, char **entry_points, size_t entry_point_count, assert(entry_point_count > 0); init_comp_scratch_mem(new_comp); - new_comp->mem_top = new_comp->scratch_mem_stack_top; - find_comp_entry_points(entry_points, entry_point_count, new_comp); resolve_rela_syms(new_comp); + resolve_comp_tls_regions(new_comp); + + // Compartment size sanity check + assert(new_comp->mem_top + == (char *) new_comp->base + // base compartment address + new_comp->size + // size of loaded ELF files + new_comp->page_size + + // buffer between scratch memory and compartment libraries + new_comp->scratch_mem_size // size of scratch memory + ); return new_comp; } @@ -180,7 +200,7 @@ comp_map(struct Compartment *to_map) MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0); if (map_result == MAP_FAILED) { - err(1, "Error mapping library %s dependency segment idx %zu!\n", + err(1, "Error mapping library %s dependency segment idx %zu", lib_dep->lib_name, j); } do_pread(lib_dep_fd, @@ -191,35 +211,22 @@ comp_map(struct Compartment *to_map) close(lib_dep_fd); } - // Map compartment scratch memory + // Map compartment scratch memory - heap, stack, sealed manager + // capabilities for transition out, capabilities to call other compartments + // (TODO fix this), TLS region (if applicable) map_result = mmap((void *) to_map->scratch_mem_base, to_map->scratch_mem_size, PROT_READ | PROT_WRITE, // | PROT_EXEC, // TODO Fix this MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0); if (map_result == MAP_FAILED) { - err(1, "Error mapping compartment %zu scratch memory!\n", to_map->id); + err(1, "Error mapping compartment %zu scratch memory", to_map->id); } - // Map compartment stack - map_result = mmap( - (char *) to_map->scratch_mem_stack_top - to_map->scratch_mem_stack_size, - to_map->scratch_mem_stack_size, - PROT_READ | PROT_WRITE | PROT_EXEC, // TODO fix this - MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS | MAP_STACK, -1, 0); - to_map->stack_pointer = to_map->scratch_mem_stack_top; - if (map_result == MAP_FAILED) - { - err(1, "Error mapping compartment %zu stack!\n", to_map->id); - } - - // Inject manager transfer function - memcpy(to_map->mng_trans_fn, (void *) &compartment_transition_out, - to_map->mng_trans_fn_sz); - - // Bind `.got.plt` entries + size_t tls_allocd = 0x0; for (size_t i = 0; i < to_map->libs_count; ++i) { + // Bind `.got.plt` entries for (size_t j = 0; j < to_map->libs[i]->rela_maps_count; ++j) { assert(to_map->libs[i]->rela_maps[j].rela_address != 0); @@ -231,6 +238,15 @@ comp_map(struct Compartment *to_map) &to_map->libs[i]->rela_maps[j].target_func_address, sizeof(void *)); } + + // Map .tdata sections + if (to_map->libs[i]->tls_data_size != 0) + { + assert(to_map->libs[i]->tls_sec_addr); + memcpy((char *) to_map->libs_tls_sects->region_start + tls_allocd, + to_map->libs[i]->tls_sec_addr, to_map->libs[i]->tls_data_size); + tls_allocd += to_map->libs[i]->tls_sec_size; + } } to_map->mapped = true; @@ -293,8 +309,13 @@ comp_exec( /*arg = cheri_perms_and(arg, !(CHERI_PERM_STORE | CHERI_PERM_EXECUTE));*/ /*args_caps[i] = arg;*/ /*}*/ - result = comp_exec_in(to_exec->stack_pointer, to_exec->ddc, fn, args, - args_count, sealed_redirect_cap); + // TODO + // * set TPIDR_EL0 to TLS start, if given + // * make `tls_lookup_stub` get the index + // * fix statics? + result = comp_exec_in(to_exec->scratch_mem_stack_top, to_exec->ddc, fn, + args, args_count, sealed_redirect_cap, + to_exec->libs_tls_sects->region_start); return result; } @@ -345,6 +366,11 @@ comp_clean(struct Compartment *to_clean) } free(to_clean->libs); free(to_clean->entry_points); + if (to_clean->libs_tls_sects) + { + free(to_clean->libs_tls_sects->lib_idxs); + free(to_clean->libs_tls_sects); + } free(to_clean); } @@ -413,6 +439,10 @@ parse_lib_file(char *lib_name, struct Compartment *new_comp) new_lib->rela_maps_count = 0; new_lib->rela_maps = NULL; + new_lib->tls_sec_addr = 0x0; + new_lib->tls_sec_size = 0; + new_lib->tls_data_size = 0; + parse_lib_segs(&lib_ehdr, lib_fd, new_lib, new_comp); // Load `.shstr` section, so we can check section names @@ -441,7 +471,7 @@ parse_lib_file(char *lib_name, struct Compartment *new_comp) // XXX According to the ELF specification version 1.2, for UNIX, there are // only one of each `SHT_SYMTAB`, `SHT_DYNSYM`, and `SHT_DYNAMIC`. Further, // we assume there can only be one section with the name `.rela.plt`. - // Therefore, we expect each `if` to be only entered once. However, we not + // Therefore, we expect each `if` to be only entered once. However, we note // that this can be changed in future specifications. // // Source: https://refspecs.linuxfoundation.org/elf/elf.pdf @@ -477,10 +507,12 @@ parse_lib_file(char *lib_name, struct Compartment *new_comp) parse_lib_dynamic_deps(&curr_shdr, &lib_ehdr, lib_fd, new_lib); found_headers += 1; } - - if (headers_of_interest_count == found_headers) + // Section containing TLS static data + else if (curr_shdr.sh_type == SHT_PROGBITS + && curr_shdr.sh_flags & SHF_TLS) { - break; + assert(new_lib->tls_sec_addr); + new_lib->tls_data_size = curr_shdr.sh_size; } } assert(headers_of_interest_count == found_headers); @@ -506,6 +538,17 @@ parse_lib_segs(Elf64_Ehdr *lib_ehdr, int lib_fd, struct LibDependency *lib_dep, { do_pread(lib_fd, &lib_phdr, sizeof(Elf64_Phdr), lib_ehdr->e_phoff + i * sizeof(lib_phdr)); + + if (lib_phdr.p_type == PT_TLS) + { + if (!new_comp->libs_tls_sects) + { + new_comp->libs_tls_sects = malloc(sizeof(struct TLSDesc)); + } + lib_dep->tls_sec_addr = (void *) lib_phdr.p_vaddr; + lib_dep->tls_sec_size = lib_phdr.p_memsz; + } + if (lib_phdr.p_type != PT_LOAD) { continue; @@ -537,6 +580,11 @@ parse_lib_segs(Elf64_Ehdr *lib_ehdr, int lib_fd, struct LibDependency *lib_dep, (char *) new_comp->mem_top + new_comp->page_size, new_comp->page_size); new_comp->size += lib_dep->lib_segs_size; new_comp->mem_top = (char *) lib_dep->lib_mem_base + lib_dep->lib_segs_size; + if (lib_dep->tls_sec_addr) + { + lib_dep->tls_sec_addr = (char *) lib_dep->tls_sec_addr + + (uintptr_t) lib_dep->lib_mem_base; + } } static void @@ -608,8 +656,15 @@ parse_lib_rela(Elf64_Shdr *rela_shdr, Elf64_Ehdr *lib_ehdr, int lib_fd, char *dyn_str_tbl = malloc(dyn_str_hdr.sh_size); do_pread(lib_fd, dyn_str_tbl, dyn_str_hdr.sh_size, dyn_str_hdr.sh_offset); + // XXX Since TLSDESC entries might resolve to two relocation slots, we + // ensure we have enough space by doubling the expected relocation counts struct LibRelaMapping *new_relas - = malloc(rela_count * sizeof(struct LibRelaMapping)); + = malloc(2 * rela_count * sizeof(struct LibRelaMapping)); + + // Prepare TLS look-up function relocation (will be copied for each TLS + // relocation entry + static struct LibRelaMapping tls_lrm + = { NULL, 0x0, 0x0, STT_FUNC, STB_GLOBAL }; // Log symbols that will need to be relocated eagerly at maptime Elf64_Rela curr_rela; @@ -618,56 +673,136 @@ parse_lib_rela(Elf64_Shdr *rela_shdr, Elf64_Ehdr *lib_ehdr, int lib_fd, { curr_rela = rela_sec[j]; size_t curr_rela_sym_idx = ELF64_R_SYM(curr_rela.r_info); + size_t curr_rela_type = ELF64_R_TYPE(curr_rela.r_info); struct LibRelaMapping lrm = { NULL, 0x0, 0x0, -1, -1 }; - if (curr_rela_sym_idx != 0) - { - Elf64_Sym curr_rela_sym = dyn_sym_tbl[curr_rela_sym_idx]; - // Filter out some `libc` symbols we don't want to handle - // TODO at least right now - if (!strcmp(&dyn_str_tbl[curr_rela_sym.st_name], "environ")) + // XXX We handle `TLS` symbols differently. It seems the way + // AARCH64 handles TLS variables is preferentially via + // `R_AARCH64_TLSDESC` entries, or more commonly known as TLS + // descriptors. We will focus on "General Dynamic" model. For this, + // each TLS variable relocates **two** slots in the GOT - the first + // is the address of a function to do the TLS lookup, usually + // against a data structure containing all TLS info, and the second + // is the parameter passed to that function. We will use our own + // function in `comp_utils` to do the lookup, `tls_rtld_dropin`, + // and simplify the process slightly by just recording the eagerly + // relocated address of the TLS variables (NB we enforce the number + // of threads, if different than one, is known at map time, so we + // don't need to dynamically handle TLS regions) + // + // Sources: + // * Speeding Up Thread-Local Storage Access in Dynamic Libraries + // in the ARM platform + // [https://www.fsfla.org/~lxoliva/writeups/TLS/paper-lk2006.pdf] + // * ELF for the ArmĀ® 64-bit Architecture (AArch64) - 2023Q3 + // [https://github.com/ARM-software/abi-aa/releases/download/2023Q3/aaelf64.pdf] + // * All about thread-local storage + // [https://maskray.me/blog/2021-02-14-all-about-thread-local-storage] + // * ELF Handling For Thread-Local Storage + // [https://www.akkadia.org/drepper/tls.pdf] + // A Deep dive into (implicit) Thread Local Storage + // [https://chao-tic.github.io/blog/2018/12/25/tls] + // + // TODO probably more types? + if (curr_rela_type == R_AARCH64_TLSDESC) + { + // Add relocation entry for TLS lookup function + memcpy(new_relas + actual_relas, &tls_lrm, + sizeof(struct LibRelaMapping)); + new_relas[actual_relas].rela_name + = malloc(strlen(tls_rtld_dropin) + 1); + strcpy(new_relas[actual_relas].rela_name, tls_rtld_dropin); + new_relas[actual_relas].rela_address + = curr_rela.r_offset + (char *) lib_dep->lib_mem_base; + actual_relas += 1; + + // Add relocation entry for actual TLS variable + if (curr_rela_sym_idx == 0) { - warnx("Currently not relocating symbol `environ` from library " - "%s - " - "using within a container might cause a crash.", - lib_dep->lib_name); - continue; + lrm.rela_sym_type = STT_TLS; + lrm.rela_sym_bind = STB_GLOBAL; // TODO help + lrm.target_func_address = (void *) curr_rela.r_addend; } - else if (!strcmp(&dyn_str_tbl[curr_rela_sym.st_name], "__progname")) + else { - warnx("Currently not relocating symbol `__progname` from " - "library %s " - "- using within a container might cause a crash.", - lib_dep->lib_name); - continue; + Elf64_Sym curr_rela_sym = dyn_sym_tbl[curr_rela_sym_idx]; + lrm.rela_sym_type = ELF64_ST_TYPE(curr_rela_sym.st_info); + lrm.rela_sym_bind = ELF64_ST_BIND(curr_rela_sym.st_info); + lrm.target_func_address = (void *) curr_rela_sym.st_value; } - lrm.rela_name - = malloc(strlen(&dyn_str_tbl[curr_rela_sym.st_name]) + 1); - strcpy(lrm.rela_name, &dyn_str_tbl[curr_rela_sym.st_name]); - lrm.rela_sym_type = ELF64_ST_TYPE(curr_rela_sym.st_info); - lrm.rela_sym_bind = ELF64_ST_BIND(curr_rela_sym.st_info); - if (curr_rela_sym.st_value != 0) - { - new_relas[actual_relas].target_func_address - = curr_rela_sym.st_value + (char *) lib_dep->lib_mem_base; - } - // TODO - assert(curr_rela.r_addend == 0 - && "I want to check if we have symbol-related relocations with " - "addends"); + // Offset relocation address by one slot, due to the lookup + // function relocation + lrm.rela_address = curr_rela.r_offset + + (char *) lib_dep->lib_mem_base + sizeof(void *); + memcpy( + new_relas + actual_relas, &lrm, sizeof(struct LibRelaMapping)); + + actual_relas += 1; + continue; } else { - lrm.target_func_address - = curr_rela.r_addend + (char *) lib_dep->lib_mem_base; - } + // Relocation entry refers to raw addresses, not a symbol entry + if (curr_rela_sym_idx == 0) + { + lrm.target_func_address + = curr_rela.r_addend + (char *) lib_dep->lib_mem_base; + } + // Relocation entry refers to a symbol entry + else + { + Elf64_Sym curr_rela_sym = dyn_sym_tbl[curr_rela_sym_idx]; - lrm.rela_address = curr_rela.r_offset + (char *) lib_dep->lib_mem_base; - memcpy(new_relas + actual_relas, &lrm, sizeof(struct LibRelaMapping)); + // Filter out some `libc` symbols we don't want to handle + // TODO at least right now + if (!strcmp(&dyn_str_tbl[curr_rela_sym.st_name], "environ")) + { + warnx("Currently not relocating symbol `environ` from " + "library " + "%s - " + "using within a container might cause a crash.", + lib_dep->lib_name); + continue; + } + else if (!strcmp( + &dyn_str_tbl[curr_rela_sym.st_name], "__progname")) + { + warnx("Currently not relocating symbol `__progname` from " + "library %s " + "- using within a container might cause a crash.", + lib_dep->lib_name); + continue; + } - actual_relas += 1; + lrm.rela_name + = malloc(strlen(&dyn_str_tbl[curr_rela_sym.st_name]) + 1); + strcpy(lrm.rela_name, &dyn_str_tbl[curr_rela_sym.st_name]); + lrm.rela_sym_type = ELF64_ST_TYPE(curr_rela_sym.st_info); + lrm.rela_sym_bind = ELF64_ST_BIND(curr_rela_sym.st_info); + if (curr_rela_sym.st_value != 0 + && lrm.rela_sym_bind != STB_WEAK) + { + lrm.target_func_address = curr_rela_sym.st_value + + (char *) lib_dep->lib_mem_base; + } + + // TODO + assert(curr_rela.r_addend == 0 + && "I want to check if we have symbol-related relocations " + "with " + "addends"); + } + lrm.rela_address + = curr_rela.r_offset + (char *) lib_dep->lib_mem_base; + memcpy( + new_relas + actual_relas, &lrm, sizeof(struct LibRelaMapping)); + + actual_relas += 1; + continue; + } + errx(1, "Unhandled relocation\n"); } lib_dep->rela_maps = realloc(lib_dep->rela_maps, (lib_dep->rela_maps_count + actual_relas) @@ -739,17 +874,33 @@ static void resolve_rela_syms(struct Compartment *new_comp) { // Find all symbols for eager relocation mapping + size_t prev_tls_secs_size = 0; for (size_t i = 0; i < new_comp->libs_count; ++i) { for (size_t j = 0; j < new_comp->libs[i]->rela_maps_count; ++j) { struct LibRelaMapping *curr_rela_map = &new_comp->libs[i]->rela_maps[j]; + + if (curr_rela_map->rela_sym_type == STT_TLS) + { + curr_rela_map->target_func_address + = (char *) curr_rela_map->target_func_address + + prev_tls_secs_size; + continue; + } + if (curr_rela_map->target_func_address != 0) { continue; } + if (!strcmp(curr_rela_map->rela_name, tls_rtld_dropin)) + { + curr_rela_map->target_func_address = new_comp->tls_lookup_func; + continue; + } + struct LibSymSearchResult found_sym = find_lib_dep_sym_in_comp(curr_rela_map->rela_name, new_comp, curr_rela_map->rela_sym_type); @@ -776,6 +927,7 @@ resolve_rela_syms(struct Compartment *new_comp) curr_rela_map->target_func_address = extract_sym_offset(new_comp, found_sym); } + prev_tls_secs_size += new_comp->libs[i]->tls_sec_size; } } @@ -809,11 +961,28 @@ find_lib_dep_sym_in_comp(const char *to_find, { for (size_t j = 0; j < comp_to_search->libs[i]->lib_syms_count; ++j) { - if (comp_to_search->libs[i]->lib_syms[j].sym_bind != STB_LOCAL + // TODO eyeball performance of this approach versus using `&&` + // Ignore `LOCAL` bind symbols - they cannot be relocated against + bool cond + = comp_to_search->libs[i]->lib_syms[j].sym_bind != STB_LOCAL; + + // Check symbol name matches + cond = cond && !strcmp( - to_find, comp_to_search->libs[i]->lib_syms[j].sym_name) - && comp_to_search->libs[i]->lib_syms[j].sym_type == sym_type - && comp_to_search->libs[i]->lib_syms[j].sym_offset != 0) + to_find, comp_to_search->libs[i]->lib_syms[j].sym_name); + + // Check symbol type matches + cond = cond + && comp_to_search->libs[i]->lib_syms[j].sym_type == sym_type; + + // Symbols cannot have 0-offset values + if (sym_type != STT_TLS) + { + cond = cond + && comp_to_search->libs[i]->lib_syms[j].sym_offset != 0; + } + + if (cond) { struct LibSymSearchResult res = { i, j }; return res; @@ -861,22 +1030,20 @@ init_comp_scratch_mem(struct Compartment *new_comp) new_comp->scratch_mem_base = align_up( (char *) new_comp->base + new_comp->size + new_comp->page_size, new_comp->page_size); - new_comp->max_manager_caps_count = 10; // TODO new_comp->scratch_mem_heap_size = 0x800000UL; // TODO - new_comp->scratch_mem_size = new_comp->scratch_mem_heap_size - + new_comp->max_manager_caps_count * sizeof(void *__capability) - + new_comp->mng_trans_fn_sz; - new_comp->scratch_mem_alloc = 0; + new_comp->scratch_mem_stack_size = 0x80000UL; // TODO new_comp->scratch_mem_stack_top = align_down( (char *) new_comp->scratch_mem_base + new_comp->scratch_mem_heap_size, 16); - new_comp->scratch_mem_stack_size = 0x80000UL; // TODO - new_comp->manager_caps = new_comp->scratch_mem_stack_top; - new_comp->active_manager_caps_count = 0; - new_comp->mng_trans_fn = (char *) new_comp->manager_caps - + new_comp->max_manager_caps_count * sizeof(void *__capability); - assert(((uintptr_t) new_comp->scratch_mem_base) % 16 == 0); + new_comp->scratch_mem_size + = new_comp->scratch_mem_heap_size + new_comp->scratch_mem_stack_size; + + new_comp->mem_top = (char *) new_comp->mem_top + + ((char *) new_comp->scratch_mem_base - (char *) new_comp->mem_top) + + new_comp->scratch_mem_size; + + assert((uintptr_t) new_comp->scratch_mem_base % new_comp->page_size == 0); assert( (((uintptr_t) new_comp->scratch_mem_base) + new_comp->scratch_mem_size) % 16 @@ -889,6 +1056,51 @@ init_comp_scratch_mem(struct Compartment *new_comp) assert(new_comp->scratch_mem_size % 16 == 0); } +static void +resolve_comp_tls_regions(struct Compartment *new_comp) +{ + if (!new_comp->libs_tls_sects) + { + return; + } + assert(new_comp->tls_lookup_func); + + // TODO currently we only support one thread + new_comp->libs_tls_sects->region_count = 1; + new_comp->libs_tls_sects->region_start = new_comp->scratch_mem_stack_top; + new_comp->libs_tls_sects->libs_count = 0; + + unsigned short *lib_idxs + = malloc(new_comp->libs_count * sizeof(unsigned short)); + unsigned short actual_idxs = 0; + size_t comp_tls_size = 0; + for (size_t i = 0; i < new_comp->libs_count; ++i) + { + if (new_comp->libs[i]->tls_sec_addr == 0x0) + { + continue; + } + comp_tls_size += new_comp->libs[i]->tls_sec_size; + new_comp->libs_tls_sects->libs_count += 1; + + lib_idxs[actual_idxs] = i; + actual_idxs += 1; + } + comp_tls_size = align_up(comp_tls_size, 16); + lib_idxs = realloc(lib_idxs, + new_comp->libs_tls_sects->libs_count * sizeof(unsigned short)); + new_comp->libs_tls_sects->lib_idxs = lib_idxs; + + intptr_t total_tls_size + = comp_tls_size * new_comp->libs_tls_sects->region_count; + new_comp->scratch_mem_size += total_tls_size; + new_comp->mem_top = (char *) new_comp->mem_top + total_tls_size; + new_comp->libs_tls_sects->region_size = comp_tls_size; + + assert((uintptr_t) new_comp->libs_tls_sects->region_start % 16 == 0); + // TODO reconsider scratch memory layout +} + /******************************************************************************* * Print functions ******************************************************************************/ @@ -929,3 +1141,55 @@ print_lib_dep(struct LibDependency *lib_dep) printf("- rela_maps_count : %zu\n", lib_dep->rela_maps_count); printf("== DONE\n"); } + +static void +print_comp(struct Compartment *to_print) +{ + printf("== COMPARTMENT\n"); + printf("- id : %lu\n", to_print->id); + { + printf("- DDC : "); + printf(" base - 0x%lx ", cheri_base_get(to_print->ddc)); + printf(" length - 0x%lx ", cheri_length_get(to_print->ddc)); + printf(" address - 0x%lx ", cheri_address_get(to_print->ddc)); + printf(" offset - 0x%lx ", cheri_offset_get(to_print->ddc)); + printf("\n"); + } + printf("- size : 0x%zx\n", to_print->size); + printf("- base : %p\n", to_print->base); + printf("- mem_top : %p\n", to_print->mem_top); + printf("- mapped : %s\n", to_print->mapped ? "true" : "false"); + + printf("- scratch_mem_base : %p\n", to_print->scratch_mem_base); + printf("- scratch_mem_size : 0x%zx", to_print->scratch_mem_size); + printf(" [0x%zx heap + 0x%zx stack + 0x%zx tls]\n", + to_print->scratch_mem_heap_size, to_print->scratch_mem_stack_size, + to_print->libs_tls_sects->region_size + * to_print->libs_tls_sects->region_count); + printf( + "- scratch_mem_heap_size : 0x%zx\n", to_print->scratch_mem_heap_size); + printf("- scratch_mem_stack_top : %p\n", to_print->scratch_mem_stack_top); + printf( + "- scratch_mem_stack_size : 0x%zx\n", to_print->scratch_mem_stack_size); + + printf("- libs_count : %lu\n", to_print->libs_count); + printf("- entry_point_count : %lu\n", to_print->entry_point_count); + // TODO entry_points + printf("- tld_lookup_func : %p\n", to_print->tls_lookup_func); + printf("- libs_tls_sects :\n"); + printf("\t> region_count : %hu\n", to_print->libs_tls_sects->region_count); + printf("\t> region_size : 0x%zx\n", to_print->libs_tls_sects->region_size); + // TODO region_start + printf("\t> region_start : %p\n", to_print->libs_tls_sects->region_start); + printf("\t> libs_count : %hu\n", to_print->libs_tls_sects->libs_count); + printf("\t> libs_idxs : "); + for (unsigned short i = 0; i < to_print->libs_tls_sects->libs_count; ++i) + { + printf("%hu,", to_print->libs_tls_sects->lib_idxs[i]); + } + printf("\n"); + + printf("- page_size : %lu\n", to_print->page_size); + + printf("== DONE\n"); +} diff --git a/src/manager.c b/src/manager.c index b58d013..21f6ac6 100644 --- a/src/manager.c +++ b/src/manager.c @@ -31,7 +31,7 @@ get_comp_with_entries(struct Compartment *); * Utility functions ******************************************************************************/ -void +static void print_full_cap(uintcap_t cap) { uint32_t words[4]; // Hack to demonstrate! In real code, be more careful @@ -45,6 +45,26 @@ print_full_cap(uintcap_t cap) printf("\n"); } +static void +pp_cap(void *__capability ptr) +{ + uint64_t length = cheri_length_get(ptr); + uint64_t address = cheri_address_get(ptr); + uint64_t base = cheri_base_get(ptr); + uint64_t flags = cheri_flags_get(ptr); + uint64_t perms = cheri_perms_get(ptr); + uint64_t type = cheri_type_get(ptr); + bool tag = cheri_tag_get(ptr); + + uint64_t offset = cheri_offset_get(ptr); + + printf("Capability: %#lp\n", ptr); + printf("Tag: %d, Perms: %04lx, Type: %lx, Address: %04lx, Base: %04lx, " + "End: %04lx, Flags: %lx, " + "Length: %04lx, Offset: %04lx\n", + tag, perms, type, address, base, base + length, flags, length, offset); +} + void * get_next_comp_addr(void) { @@ -73,9 +93,9 @@ register_new_comp(char *filename, bool allow_default_entry) filename, ep_names, new_comp_ep_count, get_next_comp_addr()); new_comp->id = comps_count; void *__capability new_comp_ddc - = cheri_address_set(cheri_ddc_get(), (uintptr_t) new_comp->base); - new_comp_ddc = cheri_bounds_set(new_comp_ddc, - (char *) new_comp->scratch_mem_stack_top - (char *) new_comp->base); + = cheri_address_set(cheri_ddc_get(), (intptr_t) new_comp->base); + new_comp_ddc = cheri_bounds_set( + new_comp_ddc, (char *) new_comp->mem_top - (char *) new_comp->base); new_comp_ddc = cheri_offset_set(new_comp_ddc, (char *) new_comp->scratch_mem_base - (char *) new_comp->base); new_comp->ddc = new_comp_ddc; diff --git a/src/transition.S b/src/transition.S index 2d82496..cd6f7b3 100644 --- a/src/transition.S +++ b/src/transition.S @@ -20,22 +20,10 @@ intercept_wrapper: ldp c28, c29, [sp], #64 ret -/* Function to transition out of a compartment; essentially the `ldpblr` - * transition instruction, and some book-keeping. - */ -.global compartment_transition_out -.type compartment_transition_out, "function" -compartment_transition_out: - // TODO - stp c29, clr, [sp, #-32]! - ldpblr c29, [c11] - ldp c29, clr, [sp], #32 - ret -compartment_transition_out_end: - /* comp_exec_in(void* comp_sp, void* __capability comp_ddc, void* fn, void* args, size_t args_count, - void* __capability sealed_redirect_cap) */ + void* __capability sealed_redirect_cap, + void* comp_tls_region_start) */ /* Instructions to enter a compartment. There is no `ret`, as we need to * perform a context switch upon exiting, which is done via `ldpbr` */ @@ -56,8 +44,11 @@ comp_exec_in: `SP + 1 * 8` > | callee-saved x19 | ` new SP` > --------------------- */ + + mrs x20, TPIDR_EL0 + stp x19, lr, [sp, #-32]! - str x29, [sp, #16] + stp x29, x20, [sp, #16] mov x19, sp // Move arguments to temporary registers (we'll need them later) @@ -95,7 +86,12 @@ loaded_params: mov x5, xzr // Transition into new compartment + // Set `TPIDR_EL0` register to base of compartment for `thread_local` + // variables (`to_exec->libs_tls_sects->region_start`) mov sp, x9 + msr TPIDR_EL0, x6 + mov x6, xzr + msr DDC, c10 // We are now fully in the new context @@ -123,5 +119,6 @@ comp_exec_out: msr DDC, c29 mov sp, x19 ldp x19, lr, [sp], #32 - ldr x29, [sp, #-16] + ldp x29, x20, [sp, #-16] + msr TPIDR_EL0, x20 ret diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 1fa35d7..c1082b0 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -120,26 +120,31 @@ set(func_binaries set(comp_binaries "simple" - "simple_libc" + "simple_call_external" "simple_call_internal" "simple_call_internal_static" "simple_call_internal_weak" - "simple_call_external" - "simple_static_var" - "simple_static_var-external" + "simple_external" + "simple_fopen" + "simple_fputs" "simple_global_var" "simple_global_var-external" - "simple_external" + "simple_libc" + "simple_malloc" + "simple_open_write" + "simple_printf" + "simple_static_var" + "simple_static_var-external" "simple_syscall_getpid" "simple_syscall_write" + "simple_thrloc_var" + "simple_thrloc_var-external" + "simple_time" "simple_va_args" - "simple_open_write" - "simple_fputs" - "simple_fopen" - "simple_malloc" - "time" + #"lua_simple" #"lua_script" + "args_simple" #"test_two_comps-comp1" #"test_two_comps-comp2 0x2000000" @@ -147,27 +152,31 @@ set(comp_binaries set(tests "simple" - "simple_libc" + "simple_call_external" "simple_call_internal" "simple_call_internal_static" "simple_call_internal_weak" - "simple_call_external" - "simple_static_var" + "simple_fopen" + "simple_fputs" "simple_global_var" + "simple_libc" + "simple_malloc" + "simple_open_write" + "simple_static_var" "simple_syscall_getpid" "simple_syscall_write" + "simple_thrloc_var" + "simple_time" "simple_va_args" - "simple_open_write" - "simple_fputs" - "simple_fopen" - "simple_malloc" - "time" + #"lua_simple" #"lua_script" + "test_map" #"test_args_near_unmapped" #"test_two_comps" #"test_two_comps_inter_call" + "args-simple args_simple check_simple 40 2" "args-more args_simple check_simple 40 2 2 2" # Check additional arguments are ignored "args-combined args_simple check_combined 400 2 20" @@ -204,6 +213,9 @@ new_dependency(simple_static_var $) target_link_libraries(simple_global_var PRIVATE simple_global_var-external) new_dependency(simple_global_var $) +target_link_libraries(simple_thrloc_var PRIVATE simple_thrloc_var-external) +new_dependency(simple_thrloc_var $) + #new_dependency(test_map $) #new_dependency(lua_script ${CMAKE_CURRENT_SOURCE_DIR}/hello_world.lua) diff --git a/tests/simple_thrloc_var-external.c b/tests/simple_thrloc_var-external.c new file mode 100644 index 0000000..fc4dba9 --- /dev/null +++ b/tests/simple_thrloc_var-external.c @@ -0,0 +1,33 @@ +#include + +_Thread_local int ex_val; +_Thread_local int ex_val_used; +_Thread_local static int ex_val_stat = 242; +_Thread_local int from_int; + +int +get_ext() +{ + ex_val = 420; + return ex_val; +} + +int +get_ext_stat() +{ + return ex_val_stat; +} + +void +use_val() +{ + ex_val_used = 24; + assert(ex_val_used == 24); +} + +void +do_ext_check(int val) +{ + from_int = val; + assert(from_int == ex_val_stat); +} diff --git a/tests/simple_thrloc_var.c b/tests/simple_thrloc_var.c new file mode 100644 index 0000000..6c2eb7c --- /dev/null +++ b/tests/simple_thrloc_var.c @@ -0,0 +1,46 @@ +#include +#include + +_Thread_local int val; +_Thread_local static int val2 = 4242; +_Thread_local static int val3 = INT_MAX; +_Thread_local static long val4 = LONG_MAX; +_Thread_local long val5; + +int +get_ext(); +int +get_ext_stat(); +void +use_val(); +void +do_ext_check(int); + +int +do_val2() +{ + assert(val2 == 4242); + return val2; +} + +int +main(void) +{ + val = 42; + int i = do_val2(); + assert(val == 42); + assert(i == 4242); + assert(val3 == INT_MAX); + long v4_local = val4; + assert(v4_local == LONG_MAX); + val5 = 21; + assert(val5 * 2 == val); + + // Check external library functions + assert(get_ext() == 420); + assert(get_ext_stat() == 242); + do_ext_check(242); + use_val(); + + return 0; +} diff --git a/tests/time.c b/tests/simple_time.c similarity index 81% rename from tests/time.c rename to tests/simple_time.c index 7a4723f..0c883b0 100644 --- a/tests/time.c +++ b/tests/simple_time.c @@ -1,4 +1,3 @@ -#include #include int