aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJens Wiklander <jens.wiklander@linaro.org>2019-07-30 08:39:27 +0200
committerJérôme Forissier <jerome.forissier@linaro.org>2019-07-30 17:49:33 +0200
commitc35dfd95ce514b5619cc56ddc7ee37b6fe44733e (patch)
treed2f16509464506fd750b888c4c0348c650c1aaec
parentd0d82b526534f8680ca0d3a3ac5014bfa39e35ac (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.c6
-rw-r--r--ldelf/ta_elf.c147
-rw-r--r--ldelf/ta_elf.h7
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);