diff options
author | Jens Wiklander <jens.wiklander@linaro.org> | 2019-07-30 08:39:27 +0200 |
---|---|---|
committer | Jérôme Forissier <jerome.forissier@linaro.org> | 2019-07-30 17:49:33 +0200 |
commit | c35dfd95ce514b5619cc56ddc7ee37b6fe44733e (patch) | |
tree | d2f16509464506fd750b888c4c0348c650c1aaec | |
parent | d0d82b526534f8680ca0d3a3ac5014bfa39e35ac (diff) |
ldelf: accurate non-legacy TA check
Prior to this patch ldelf relied on e_entry in the ELF header to point
anywhere but at the lowest possible executable address to tell that it's
not a legacy TA. This isn't enough since all TAs can use this entry
address depending on compiler and linker. The only reliable indicator
that the TA is not a legacy TA is that the depr_entry field of the TA
header is UINT64_MAX. Unfortunately this means assuming that the ELF is
not a legacy TA at load time and that the process needs to be restarted
in case it turns out that it was a legacy TA.
With this patch we have reliable detection of non-legacy TAs, but with
increased load time for legacy TAs since the main ELF needs to be loaded
and verified twice due to the TA store interface.
Acked-by: Jerome Forissier <jerome.forissier@linaro.org>
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
-rw-r--r-- | ldelf/main.c | 6 | ||||
-rw-r--r-- | ldelf/ta_elf.c | 147 | ||||
-rw-r--r-- | ldelf/ta_elf.h | 7 |
3 files changed, 117 insertions, 43 deletions
diff --git a/ldelf/main.c b/ldelf/main.c index ecc07adb..b3dac543 100644 --- a/ldelf/main.c +++ b/ldelf/main.c @@ -123,8 +123,8 @@ void ldelf(struct ldelf_arg *arg) malloc_add_pool((void *)mpool_base, mpool_size); /* Load the main binary and get a list of dependencies, if any. */ - ta_elf_load_main(&arg->uuid, &arg->is_32bit, &arg->entry_func, - &arg->stack_ptr, &arg->flags); + ta_elf_load_main(&arg->uuid, &arg->is_32bit, &arg->stack_ptr, + &arg->flags); /* * Load binaries, ta_elf_load() may add external libraries to the @@ -139,6 +139,8 @@ void ldelf(struct ldelf_arg *arg) ta_elf_finalize_mappings(elf); } + ta_elf_finalize_load_main(&arg->entry_func); + arg->ftrace_entry = 0; #ifdef CFG_TA_FTRACE_SUPPORT if (ftrace_init()) diff --git a/ldelf/ta_elf.c b/ldelf/ta_elf.c index 9d187c7f..5dc82ca3 100644 --- a/ldelf/ta_elf.c +++ b/ldelf/ta_elf.c @@ -452,6 +452,7 @@ static void populate_segments_legacy(struct ta_elf *elf) struct segment *seg = NULL; vaddr_t va = 0; + assert(elf->is_legacy); TAILQ_FOREACH(seg, &elf->segs, link) { struct segment *last_seg = TAILQ_LAST(&elf->segs, segment_head); size_t pad_end = roundup(last_seg->vaddr + last_seg->memsz - @@ -516,6 +517,7 @@ static void populate_segments(struct ta_elf *elf) vaddr_t va = 0; size_t pad_begin = 0; + assert(!elf->is_legacy); TAILQ_FOREACH(seg, &elf->segs, link) { struct segment *last_seg = TAILQ_LAST(&elf->segs, segment_head); size_t pad_end = roundup(last_seg->vaddr + last_seg->memsz - @@ -672,10 +674,6 @@ static void map_segments(struct ta_elf *elf) elf->max_addr = va + sz; elf->phdr = (void *)(va + elf->e_phoff); } - if (elf->is_legacy) - populate_segments_legacy(elf); - else - populate_segments(elf); } static int hex(char c) @@ -849,64 +847,134 @@ static void close_handle(struct ta_elf *elf) elf->handle = -1; } -void ta_elf_load_main(const TEE_UUID *uuid, uint32_t *is_32bit, - uint64_t *entry, uint64_t *sp, uint32_t *ta_flags) +static void clean_elf_load_main(struct ta_elf *elf) { - struct ta_elf *elf = queue_elf(uuid); - struct ta_head *head; - vaddr_t va = 0; TEE_Result res = TEE_SUCCESS; - assert(elf); - elf->is_main = true; - - init_elf(elf); - /* - * Legacy TAs doesn't set entry point, instead it's set in ta_head. - * If entry point isn't set explicitly, set to the start of the - * first executable section by the linker. Since ta_head also - * always comes first in legacy TA it means that the entry point - * will be set to 0x20. - * - * NB, everything before the commit a73b5878c89d ("Replace - * ta_head.entry with elf entry") is considered legacy TAs for - * ldelf. + * Clean up from last attempt to load */ - if (elf->e_entry == sizeof(*head)) - elf->is_legacy = true; + res = sys_unmap(elf->ehdr_addr, SMALL_PAGE_SIZE); + if (res) + err(res, "sys_unmap"); + while (!TAILQ_EMPTY(&elf->segs)) { + struct segment *seg = TAILQ_FIRST(&elf->segs); + vaddr_t va = 0; + size_t num_bytes = 0; + + va = rounddown(elf->load_addr + seg->vaddr); + if (seg->remapped_writeable) + num_bytes = roundup(seg->vaddr + seg->memsz) - + rounddown(seg->vaddr); + else + num_bytes = seg->memsz; + + res = sys_unmap(va, num_bytes); + if (res) + err(res, "sys_unmap"); + + TAILQ_REMOVE(&elf->segs, seg, link); + free(seg); + } + + free(elf->shdr); + memset(&elf->is_32bit, 0, + (vaddr_t)&elf->uuid - (vaddr_t)&elf->is_32bit); + + TAILQ_INIT(&elf->segs); +} + +static void load_main(struct ta_elf *elf) +{ + init_elf(elf); map_segments(elf); + populate_segments(elf); add_dependencies(elf); copy_section_headers(elf); save_symtab(elf); close_handle(elf); - head = (struct ta_head *)elf->load_addr; + elf->head = (struct ta_head *)elf->load_addr; + if (elf->head->depr_entry != UINT64_MAX) { + /* + * Legacy TAs sets their entry point in ta_head. For + * non-legacy TAs the entry point of the ELF is set instead + * and leaving the ta_head entry point set to UINT64_MAX to + * indicate that it's not used. + * + * NB, everything before the commit a73b5878c89d ("Replace + * ta_head.entry with elf entry") is considered legacy TAs + * for ldelf. + * + * Legacy TAs cannot be mapped with shared memory segments + * so restart the mapping if it turned out we're loading a + * legacy TA. + */ - *is_32bit = elf->is_32bit; - if (elf->is_legacy) { - assert(head->depr_entry != UINT64_MAX); - *entry = head->depr_entry + elf->load_addr; - } else { - assert(head->depr_entry == UINT64_MAX); - *entry = elf->e_entry + elf->load_addr; + DMSG("Reloading TA %pUl as legacy TA", (void *)&elf->uuid); + clean_elf_load_main(elf); + elf->is_legacy = true; + init_elf(elf); + map_segments(elf); + populate_segments_legacy(elf); + add_dependencies(elf); + copy_section_headers(elf); + save_symtab(elf); + close_handle(elf); + elf->head = (struct ta_head *)elf->load_addr; + /* + * Check that the TA is still a legacy TA, if it isn't give + * up now since we're likely under attack. + */ + if (elf->head->depr_entry == UINT64_MAX) + err(TEE_ERROR_GENERIC, + "TA %pUl was changed on disk to non-legacy", + (void *)&elf->uuid); } - res = sys_map_zi(head->stack_size, 0, &va, 0, 0); +} + +void ta_elf_load_main(const TEE_UUID *uuid, uint32_t *is_32bit, uint64_t *sp, + uint32_t *ta_flags) +{ + struct ta_elf *elf = queue_elf(uuid); + vaddr_t va = 0; + TEE_Result res = TEE_SUCCESS; + + assert(elf); + elf->is_main = true; + + load_main(elf); + + *is_32bit = elf->is_32bit; + res = sys_map_zi(elf->head->stack_size, 0, &va, 0, 0); if (res) err(res, "sys_map_zi stack"); - if (head->flags & ~TA_FLAGS_MASK) + if (elf->head->flags & ~TA_FLAGS_MASK) err(TEE_ERROR_BAD_FORMAT, "Invalid TA flags(s) %#"PRIx32, - head->flags & ~TA_FLAGS_MASK); + elf->head->flags & ~TA_FLAGS_MASK); - *ta_flags = head->flags; - *sp = va + head->stack_size; + *ta_flags = elf->head->flags; + *sp = va + elf->head->stack_size; ta_stack = va; - ta_stack_size = head->stack_size; + ta_stack_size = elf->head->stack_size; +} + +void ta_elf_finalize_load_main(uint64_t *entry) +{ + struct ta_elf *elf = TAILQ_FIRST(&main_elf_queue); + + assert(elf->is_main); + + if (elf->is_legacy) + *entry = elf->head->depr_entry; + else + *entry = elf->e_entry + elf->load_addr; } + void ta_elf_load_dependency(struct ta_elf *elf, bool is_32bit) { if (elf->is_main) @@ -919,6 +987,7 @@ void ta_elf_load_dependency(struct ta_elf *elf, bool is_32bit) is_32bit ? "32" : "64"); map_segments(elf); + populate_segments(elf); add_dependencies(elf); copy_section_headers(elf); save_symtab(elf); diff --git a/ldelf/ta_elf.h b/ldelf/ta_elf.h index f3e3d3eb..50e72274 100644 --- a/ldelf/ta_elf.h +++ b/ldelf/ta_elf.h @@ -65,6 +65,8 @@ struct ta_elf { uint32_t handle; + struct ta_head *head; + TEE_UUID uuid; TAILQ_ENTRY(ta_elf) link; }; @@ -76,8 +78,9 @@ typedef void (*print_func_t)(void *pctx, const char *fmt, va_list ap) extern struct ta_elf_queue main_elf_queue; -void ta_elf_load_main(const TEE_UUID *uuid, uint32_t *is_32bit, - uint64_t *entry, uint64_t *sp, uint32_t *ta_flags); +void ta_elf_load_main(const TEE_UUID *uuid, uint32_t *is_32bit, uint64_t *sp, + uint32_t *ta_flags); +void ta_elf_finalize_load_main(uint64_t *entry); void ta_elf_load_dependency(struct ta_elf *elf, bool is_32bit); void ta_elf_relocate(struct ta_elf *elf); void ta_elf_finalize_mappings(struct ta_elf *elf); |