diff options
author | Balint Dobszay <balint.dobszay@arm.com> | 2020-12-11 16:25:25 +0100 |
---|---|---|
committer | Jérôme Forissier <jerome@forissier.org> | 2021-01-05 17:56:19 +0100 |
commit | cbe7e1b87977e7dff91e859f5ff0c839b9387c4a (patch) | |
tree | a1c94e9726c74f028a6bbdf08ba552cf303a6067 /core | |
parent | 36bb435f5be571b4b4c0792de1cea85013afa728 (diff) |
core: extract ldelf related code from system PTA
Various functions in the system PTA are only used in conjunction with
ldelf. They either implement a system service needed only by ldelf
(system_open_ta_binary(), etc.) or they provide an interface for TAs to
invoke services implemented in user space by ldelf (call_ldelf_dlopen(),
call_ldelf_dlsym()). Extract these to a separate file as a first step
towards converting the PTA system_*() calls into proper ldelf-specific
syscalls.
Acked-by: Etienne Carriere <etienne.carriere@linaro.org>
Acked-by: Jerome Forissier <jerome@forissier.org>
Reviewed-by: Jens Wiklander <jens.wiklander@linaro.org>
Signed-off-by: Balint Dobszay <balint.dobszay@arm.com>
Diffstat (limited to 'core')
-rw-r--r-- | core/arch/arm/include/kernel/ldelf_loader.h | 4 | ||||
-rw-r--r-- | core/arch/arm/kernel/ldelf_loader.c | 92 | ||||
-rw-r--r-- | core/include/kernel/ldelf_syscalls.h | 39 | ||||
-rw-r--r-- | core/kernel/ldelf_syscalls.c | 459 | ||||
-rw-r--r-- | core/kernel/sub.mk | 1 | ||||
-rw-r--r-- | core/pta/system.c | 565 |
6 files changed, 605 insertions, 555 deletions
diff --git a/core/arch/arm/include/kernel/ldelf_loader.h b/core/arch/arm/include/kernel/ldelf_loader.h index 68e48ea8..aa5bb82b 100644 --- a/core/arch/arm/include/kernel/ldelf_loader.h +++ b/core/arch/arm/include/kernel/ldelf_loader.h @@ -15,5 +15,9 @@ TEE_Result ldelf_init_with_ldelf(struct ts_session *sess, TEE_Result ldelf_dump_state(struct user_mode_ctx *uctx); TEE_Result ldelf_dump_ftrace(struct user_mode_ctx *uctx, void *buf, size_t *blen); +TEE_Result ldelf_dlopen(struct user_mode_ctx *uctx, TEE_UUID *uuid, + uint32_t flags); +TEE_Result ldelf_dlsym(struct user_mode_ctx *uctx, TEE_UUID *uuid, + const char *sym, size_t maxlen, vaddr_t *val); #endif /* KERNEL_LDELF_LOADER_H */ diff --git a/core/arch/arm/kernel/ldelf_loader.c b/core/arch/arm/kernel/ldelf_loader.c index 3b2c9907..eaeee61d 100644 --- a/core/arch/arm/kernel/ldelf_loader.c +++ b/core/arch/arm/kernel/ldelf_loader.c @@ -318,3 +318,95 @@ TEE_Result ldelf_dump_ftrace(struct user_mode_ctx *uctx, return res; } #endif /*CFG_FTRACE_SUPPORT*/ + +TEE_Result ldelf_dlopen(struct user_mode_ctx *uctx, TEE_UUID *uuid, + uint32_t flags) +{ + uaddr_t usr_stack = uctx->ldelf_stack_ptr; + TEE_Result res = TEE_ERROR_GENERIC; + struct dl_entry_arg *arg = NULL; + uint32_t panic_code = 0; + uint32_t panicked = 0; + + assert(uuid); + + usr_stack -= ROUNDUP(sizeof(*arg), STACK_ALIGNMENT); + arg = (struct dl_entry_arg *)usr_stack; + + res = vm_check_access_rights(uctx, + TEE_MEMORY_ACCESS_READ | + TEE_MEMORY_ACCESS_WRITE | + TEE_MEMORY_ACCESS_ANY_OWNER, + (uaddr_t)arg, sizeof(*arg)); + if (res) { + EMSG("ldelf stack is inaccessible!"); + return res; + } + + memset(arg, 0, sizeof(*arg)); + arg->cmd = LDELF_DL_ENTRY_DLOPEN; + arg->dlopen.uuid = *uuid; + arg->dlopen.flags = flags; + + res = thread_enter_user_mode((vaddr_t)arg, 0, 0, 0, + usr_stack, uctx->dl_entry_func, + is_arm32, &panicked, &panic_code); + if (panicked) { + EMSG("ldelf dl_entry function panicked"); + abort_print_current_ta(); + res = TEE_ERROR_TARGET_DEAD; + } + if (!res) + res = arg->ret; + + return res; +} + +TEE_Result ldelf_dlsym(struct user_mode_ctx *uctx, TEE_UUID *uuid, + const char *sym, size_t maxlen, vaddr_t *val) +{ + uaddr_t usr_stack = uctx->ldelf_stack_ptr; + TEE_Result res = TEE_ERROR_GENERIC; + struct dl_entry_arg *arg = NULL; + uint32_t panic_code = 0; + uint32_t panicked = 0; + size_t len = strnlen(sym, maxlen); + + if (len == maxlen) + return TEE_ERROR_BAD_PARAMETERS; + + usr_stack -= ROUNDUP(sizeof(*arg) + len + 1, STACK_ALIGNMENT); + arg = (struct dl_entry_arg *)usr_stack; + + res = vm_check_access_rights(uctx, + TEE_MEMORY_ACCESS_READ | + TEE_MEMORY_ACCESS_WRITE | + TEE_MEMORY_ACCESS_ANY_OWNER, + (uaddr_t)arg, sizeof(*arg) + len + 1); + if (res) { + EMSG("ldelf stack is inaccessible!"); + return res; + } + + memset(arg, 0, sizeof(*arg)); + arg->cmd = LDELF_DL_ENTRY_DLSYM; + arg->dlsym.uuid = *uuid; + memcpy(arg->dlsym.symbol, sym, len); + arg->dlsym.symbol[len] = '\0'; + + res = thread_enter_user_mode((vaddr_t)arg, 0, 0, 0, + usr_stack, uctx->dl_entry_func, + is_arm32, &panicked, &panic_code); + if (panicked) { + EMSG("ldelf dl_entry function panicked"); + abort_print_current_ta(); + res = TEE_ERROR_TARGET_DEAD; + } + if (!res) { + res = arg->ret; + if (!res) + *val = arg->dlsym.val; + } + + return res; +} diff --git a/core/include/kernel/ldelf_syscalls.h b/core/include/kernel/ldelf_syscalls.h new file mode 100644 index 00000000..c1473ca6 --- /dev/null +++ b/core/include/kernel/ldelf_syscalls.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2018-2019, Linaro Limited + * Copyright (c) 2020, Arm Limited + */ + +#include <kernel/handle.h> +#include <kernel/ts_store.h> +#include <kernel/user_mode_ctx_struct.h> +#include <tee_api_defines.h> +#include <tee_api_types.h> +#include <types_ext.h> + +#ifndef KERNEL_LDELF_SYSCALLS_H +#define KERNEL_LDELF_SYSCALLS_H + +struct system_ctx { + struct handle_db db; + const struct ts_store_ops *store_op; +}; + +void ta_bin_close(void *ptr); +TEE_Result ldelf_open_ta_binary(struct system_ctx *ctx, uint32_t param_types, + TEE_Param params[TEE_NUM_PARAMS]); +TEE_Result ldelf_close_ta_binary(struct system_ctx *ctx, uint32_t param_types, + TEE_Param params[TEE_NUM_PARAMS]); +TEE_Result ldelf_map_ta_binary(struct system_ctx *ctx, + struct user_mode_ctx *uctx, + uint32_t param_types, + TEE_Param params[TEE_NUM_PARAMS]); +TEE_Result ldelf_copy_from_ta_binary(struct system_ctx *ctx, + uint32_t param_types, + TEE_Param params[TEE_NUM_PARAMS]); +TEE_Result ldelf_set_prot(struct user_mode_ctx *uctx, uint32_t param_types, + TEE_Param params[TEE_NUM_PARAMS]); +TEE_Result ldelf_remap(struct user_mode_ctx *uctx, uint32_t param_types, + TEE_Param params[TEE_NUM_PARAMS]); + +#endif /* KERNEL_LDELF_SYSCALLS_H */ diff --git a/core/kernel/ldelf_syscalls.c b/core/kernel/ldelf_syscalls.c new file mode 100644 index 00000000..3a563fa4 --- /dev/null +++ b/core/kernel/ldelf_syscalls.c @@ -0,0 +1,459 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2018-2019, Linaro Limited + * Copyright (c) 2020, Arm Limited + */ + +#include <assert.h> +#include <kernel/ldelf_syscalls.h> +#include <kernel/user_mode_ctx.h> +#include <ldelf.h> +#include <mm/file.h> +#include <mm/fobj.h> +#include <mm/mobj.h> +#include <mm/vm.h> +#include <pta_system.h> +#include <stdlib.h> +#include <string.h> +#include <trace.h> +#include <util.h> + +struct bin_handle { + const struct ts_store_ops *op; + struct ts_store_handle *h; + struct file *f; + size_t offs_bytes; + size_t size_bytes; +}; + +void ta_bin_close(void *ptr) +{ + struct bin_handle *binh = ptr; + + if (binh) { + if (binh->op && binh->h) + binh->op->close(binh->h); + file_put(binh->f); + } + free(binh); +} + +TEE_Result ldelf_open_ta_binary(struct system_ctx *ctx, uint32_t param_types, + TEE_Param params[TEE_NUM_PARAMS]) +{ + TEE_Result res = TEE_SUCCESS; + struct bin_handle *binh = NULL; + int h = 0; + TEE_UUID *uuid = NULL; + uint8_t tag[FILE_TAG_SIZE] = { 0 }; + unsigned int tag_len = sizeof(tag); + uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INPUT, + TEE_PARAM_TYPE_VALUE_OUTPUT, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE); + + if (exp_pt != param_types) + return TEE_ERROR_BAD_PARAMETERS; + if (params[0].memref.size != sizeof(*uuid)) + return TEE_ERROR_BAD_PARAMETERS; + + uuid = params[0].memref.buffer; + + binh = calloc(1, sizeof(*binh)); + if (!binh) + return TEE_ERROR_OUT_OF_MEMORY; + + SCATTERED_ARRAY_FOREACH(binh->op, ta_stores, struct ts_store_ops) { + DMSG("Lookup user TA ELF %pUl (%s)", + (void *)uuid, binh->op->description); + + res = binh->op->open(uuid, &binh->h); + DMSG("res=0x%x", res); + if (res != TEE_ERROR_ITEM_NOT_FOUND && + res != TEE_ERROR_STORAGE_NOT_AVAILABLE) + break; + } + if (res) + goto err; + + res = binh->op->get_size(binh->h, &binh->size_bytes); + if (res) + goto err; + res = binh->op->get_tag(binh->h, tag, &tag_len); + if (res) + goto err; + binh->f = file_get_by_tag(tag, tag_len); + if (!binh->f) + goto err_oom; + + h = handle_get(&ctx->db, binh); + if (h < 0) + goto err_oom; + params[0].value.a = h; + + return TEE_SUCCESS; +err_oom: + res = TEE_ERROR_OUT_OF_MEMORY; +err: + ta_bin_close(binh); + return res; +} + +TEE_Result ldelf_close_ta_binary(struct system_ctx *ctx, uint32_t param_types, + TEE_Param params[TEE_NUM_PARAMS]) +{ + TEE_Result res = TEE_SUCCESS; + struct bin_handle *binh = NULL; + uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE); + + if (exp_pt != param_types) + return TEE_ERROR_BAD_PARAMETERS; + + if (params[0].value.b) + return TEE_ERROR_BAD_PARAMETERS; + + binh = handle_put(&ctx->db, params[0].value.a); + if (!binh) + return TEE_ERROR_BAD_PARAMETERS; + + if (binh->offs_bytes < binh->size_bytes) + res = binh->op->read(binh->h, NULL, + binh->size_bytes - binh->offs_bytes); + + ta_bin_close(binh); + + return res; +} + +static TEE_Result binh_copy_to(struct bin_handle *binh, vaddr_t va, + size_t offs_bytes, size_t num_bytes) +{ + TEE_Result res = TEE_SUCCESS; + size_t next_offs = 0; + + if (offs_bytes < binh->offs_bytes) + return TEE_ERROR_BAD_STATE; + + if (ADD_OVERFLOW(offs_bytes, num_bytes, &next_offs)) + return TEE_ERROR_BAD_PARAMETERS; + + if (offs_bytes > binh->offs_bytes) { + res = binh->op->read(binh->h, NULL, + offs_bytes - binh->offs_bytes); + if (res) + return res; + binh->offs_bytes = offs_bytes; + } + + if (next_offs > binh->size_bytes) { + size_t rb = binh->size_bytes - binh->offs_bytes; + + res = binh->op->read(binh->h, (void *)va, rb); + if (res) + return res; + memset((uint8_t *)va + rb, 0, num_bytes - rb); + binh->offs_bytes = binh->size_bytes; + } else { + res = binh->op->read(binh->h, (void *)va, num_bytes); + if (res) + return res; + binh->offs_bytes = next_offs; + } + + return TEE_SUCCESS; +} + +TEE_Result ldelf_map_ta_binary(struct system_ctx *ctx, + struct user_mode_ctx *uctx, + uint32_t param_types, + TEE_Param params[TEE_NUM_PARAMS]) +{ + const uint32_t accept_flags = PTA_SYSTEM_MAP_FLAG_SHAREABLE | + PTA_SYSTEM_MAP_FLAG_WRITEABLE | + PTA_SYSTEM_MAP_FLAG_EXECUTABLE; + uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT, + TEE_PARAM_TYPE_VALUE_INPUT, + TEE_PARAM_TYPE_VALUE_INOUT, + TEE_PARAM_TYPE_VALUE_INPUT); + struct bin_handle *binh = NULL; + uint32_t num_rounded_bytes = 0; + TEE_Result res = TEE_SUCCESS; + struct file_slice *fs = NULL; + bool file_is_locked = false; + struct mobj *mobj = NULL; + uint32_t offs_bytes = 0; + uint32_t offs_pages = 0; + uint32_t num_bytes = 0; + uint32_t pad_begin = 0; + uint32_t pad_end = 0; + size_t num_pages = 0; + uint32_t flags = 0; + uint32_t prot = 0; + vaddr_t va = 0; + + if (exp_pt != param_types) + return TEE_ERROR_BAD_PARAMETERS; + + binh = handle_lookup(&ctx->db, params[0].value.a); + if (!binh) + return TEE_ERROR_BAD_PARAMETERS; + flags = params[0].value.b; + offs_bytes = params[1].value.a; + num_bytes = params[1].value.b; + va = reg_pair_to_64(params[2].value.a, params[2].value.b); + pad_begin = params[3].value.a; + pad_end = params[3].value.b; + + if ((flags & accept_flags) != flags) + return TEE_ERROR_BAD_PARAMETERS; + + if ((flags & PTA_SYSTEM_MAP_FLAG_SHAREABLE) && + (flags & PTA_SYSTEM_MAP_FLAG_WRITEABLE)) + return TEE_ERROR_BAD_PARAMETERS; + + if ((flags & PTA_SYSTEM_MAP_FLAG_EXECUTABLE) && + (flags & PTA_SYSTEM_MAP_FLAG_WRITEABLE)) + return TEE_ERROR_BAD_PARAMETERS; + + if (offs_bytes & SMALL_PAGE_MASK) + return TEE_ERROR_BAD_PARAMETERS; + + prot = TEE_MATTR_UR | TEE_MATTR_PR; + if (flags & PTA_SYSTEM_MAP_FLAG_WRITEABLE) + prot |= TEE_MATTR_UW | TEE_MATTR_PW; + if (flags & PTA_SYSTEM_MAP_FLAG_EXECUTABLE) + prot |= TEE_MATTR_UX; + + offs_pages = offs_bytes >> SMALL_PAGE_SHIFT; + if (ROUNDUP_OVERFLOW(num_bytes, SMALL_PAGE_SIZE, &num_rounded_bytes)) + return TEE_ERROR_BAD_PARAMETERS; + num_pages = num_rounded_bytes / SMALL_PAGE_SIZE; + + if (!file_trylock(binh->f)) { + /* + * Before we can block on the file lock we must make all + * our page tables available for reclaiming in order to + * avoid a dead-lock with the other thread (which already + * is holding the file lock) mapping lots of memory below. + */ + vm_set_ctx(NULL); + file_lock(binh->f); + vm_set_ctx(uctx->ts_ctx); + } + file_is_locked = true; + fs = file_find_slice(binh->f, offs_pages); + if (fs) { + /* If there's registered slice it has to match */ + if (fs->page_offset != offs_pages || + num_pages > fs->fobj->num_pages) { + res = TEE_ERROR_BAD_PARAMETERS; + goto err; + } + + /* If there's a slice we must be mapping shareable */ + if (!(flags & PTA_SYSTEM_MAP_FLAG_SHAREABLE)) { + res = TEE_ERROR_BAD_PARAMETERS; + goto err; + } + + mobj = mobj_with_fobj_alloc(fs->fobj, binh->f); + if (!mobj) { + res = TEE_ERROR_OUT_OF_MEMORY; + goto err; + } + res = vm_map_pad(uctx, &va, num_rounded_bytes, + prot, VM_FLAG_READONLY, + mobj, 0, pad_begin, pad_end, 0); + mobj_put(mobj); + if (res) + goto err; + } else { + struct fobj *f = fobj_ta_mem_alloc(num_pages); + struct file *file = NULL; + uint32_t vm_flags = 0; + + if (!f) { + res = TEE_ERROR_OUT_OF_MEMORY; + goto err; + } + if (!(flags & PTA_SYSTEM_MAP_FLAG_WRITEABLE)) { + file = binh->f; + vm_flags |= VM_FLAG_READONLY; + } + + mobj = mobj_with_fobj_alloc(f, file); + fobj_put(f); + if (!mobj) { + res = TEE_ERROR_OUT_OF_MEMORY; + goto err; + } + res = vm_map_pad(uctx, &va, num_rounded_bytes, + TEE_MATTR_PRW, vm_flags, mobj, 0, + pad_begin, pad_end, 0); + mobj_put(mobj); + if (res) + goto err; + res = binh_copy_to(binh, va, offs_bytes, num_bytes); + if (res) + goto err_unmap_va; + res = vm_set_prot(uctx, va, num_rounded_bytes, + prot); + if (res) + goto err_unmap_va; + + /* + * The context currently is active set it again to update + * the mapping. + */ + vm_set_ctx(uctx->ts_ctx); + + if (!(flags & PTA_SYSTEM_MAP_FLAG_WRITEABLE)) { + res = file_add_slice(binh->f, f, offs_pages); + if (res) + goto err_unmap_va; + } + } + + file_unlock(binh->f); + + reg_pair_from_64(va, ¶ms[2].value.a, ¶ms[2].value.b); + return TEE_SUCCESS; + +err_unmap_va: + if (vm_unmap(uctx, va, num_rounded_bytes)) + panic(); + + /* + * The context currently is active set it again to update + * the mapping. + */ + vm_set_ctx(uctx->ts_ctx); + +err: + if (file_is_locked) + file_unlock(binh->f); + + return res; +} + +TEE_Result ldelf_copy_from_ta_binary(struct system_ctx *ctx, + uint32_t param_types, + TEE_Param params[TEE_NUM_PARAMS]) +{ + struct bin_handle *binh = NULL; + uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT, + TEE_PARAM_TYPE_MEMREF_OUTPUT, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE); + + if (exp_pt != param_types) + return TEE_ERROR_BAD_PARAMETERS; + + binh = handle_lookup(&ctx->db, params[0].value.a); + if (!binh) + return TEE_ERROR_BAD_PARAMETERS; + + return binh_copy_to(binh, (vaddr_t)params[1].memref.buffer, + params[0].value.b, params[1].memref.size); +} + +TEE_Result ldelf_set_prot(struct user_mode_ctx *uctx, uint32_t param_types, + TEE_Param params[TEE_NUM_PARAMS]) +{ + const uint32_t accept_flags = PTA_SYSTEM_MAP_FLAG_WRITEABLE | + PTA_SYSTEM_MAP_FLAG_EXECUTABLE; + uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT, + TEE_PARAM_TYPE_VALUE_INPUT, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE); + uint32_t prot = TEE_MATTR_UR | TEE_MATTR_PR; + TEE_Result res = TEE_SUCCESS; + uint32_t vm_flags = 0; + uint32_t flags = 0; + vaddr_t end_va = 0; + vaddr_t va = 0; + size_t sz = 0; + + if (exp_pt != param_types) + return TEE_ERROR_BAD_PARAMETERS; + + flags = params[0].value.b; + + if ((flags & accept_flags) != flags) + return TEE_ERROR_BAD_PARAMETERS; + if (flags & PTA_SYSTEM_MAP_FLAG_WRITEABLE) + prot |= TEE_MATTR_UW | TEE_MATTR_PW; + if (flags & PTA_SYSTEM_MAP_FLAG_EXECUTABLE) + prot |= TEE_MATTR_UX; + + va = reg_pair_to_64(params[1].value.a, params[1].value.b); + sz = ROUNDUP(params[0].value.a, SMALL_PAGE_SIZE); + + /* + * The vm_get_flags() and vm_set_prot() are supposed to detect or + * handle overflow directly or indirectly. However, this function + * an API function so an extra guard here is in order. If nothing + * else to make it easier to review the code. + */ + if (ADD_OVERFLOW(va, sz, &end_va)) + return TEE_ERROR_BAD_PARAMETERS; + + res = vm_get_flags(uctx, va, sz, &vm_flags); + if (res) + return res; + if (vm_flags & VM_FLAG_PERMANENT) + return TEE_ERROR_ACCESS_DENIED; + + /* + * If the segment is a mapping of a part of a file (vm_flags & + * VM_FLAG_READONLY) it cannot be made writeable as all mapped + * files are mapped read-only. + */ + if ((vm_flags & VM_FLAG_READONLY) && + (prot & (TEE_MATTR_UW | TEE_MATTR_PW))) + return TEE_ERROR_ACCESS_DENIED; + + return vm_set_prot(uctx, va, sz, prot); +} + +TEE_Result ldelf_remap(struct user_mode_ctx *uctx, uint32_t param_types, + TEE_Param params[TEE_NUM_PARAMS]) +{ + uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT, + TEE_PARAM_TYPE_VALUE_INPUT, + TEE_PARAM_TYPE_VALUE_INOUT, + TEE_PARAM_TYPE_VALUE_INPUT); + TEE_Result res = TEE_SUCCESS; + uint32_t num_bytes = 0; + uint32_t pad_begin = 0; + uint32_t vm_flags = 0; + uint32_t pad_end = 0; + vaddr_t old_va = 0; + vaddr_t new_va = 0; + + if (exp_pt != param_types) + return TEE_ERROR_BAD_PARAMETERS; + + num_bytes = params[0].value.a; + old_va = reg_pair_to_64(params[1].value.a, params[1].value.b); + new_va = reg_pair_to_64(params[2].value.a, params[2].value.b); + pad_begin = params[3].value.a; + pad_end = params[3].value.b; + + res = vm_get_flags(uctx, old_va, num_bytes, &vm_flags); + if (res) + return res; + if (vm_flags & VM_FLAG_PERMANENT) + return TEE_ERROR_ACCESS_DENIED; + + res = vm_remap(uctx, &new_va, old_va, num_bytes, pad_begin, + pad_end); + if (!res) + reg_pair_from_64(new_va, ¶ms[2].value.a, + ¶ms[2].value.b); + + return res; +} diff --git a/core/kernel/sub.mk b/core/kernel/sub.mk index 83512f94..59fdb19b 100644 --- a/core/kernel/sub.mk +++ b/core/kernel/sub.mk @@ -6,6 +6,7 @@ srcs-$(CFG_DT) += dt.c srcs-y += pm.c srcs-y += handle.c srcs-y += interrupt.c +srcs-$(CFG_WITH_USER_TA) += ldelf_syscalls.c srcs-$(CFG_LOCKDEP) += lockdep.c ifneq ($(CFG_CORE_FFA),y) srcs-$(CFG_CORE_DYN_SHM) += msg_param.c diff --git a/core/pta/system.c b/core/pta/system.c index 5dac3310..c4287633 100644 --- a/core/pta/system.c +++ b/core/pta/system.c @@ -8,6 +8,8 @@ #include <crypto/crypto.h> #include <kernel/handle.h> #include <kernel/huk_subkey.h> +#include <kernel/ldelf_loader.h> +#include <kernel/ldelf_syscalls.h> #include <kernel/misc.h> #include <kernel/msg_param.h> #include <kernel/pseudo_ta.h> @@ -26,19 +28,6 @@ #include <tee_api_defines.h> #include <util.h> -struct bin_handle { - const struct ts_store_ops *op; - struct ts_store_handle *h; - struct file *f; - size_t offs_bytes; - size_t size_bytes; -}; - -struct system_ctx { - struct handle_db db; - const struct ts_store_ops *store_op; -}; - static unsigned int system_pnum; static TEE_Result system_rng_reseed(uint32_t param_types, @@ -210,539 +199,6 @@ static TEE_Result system_unmap(struct user_mode_ctx *uctx, uint32_t param_types, return vm_unmap(uctx, va, sz); } -static void ta_bin_close(void *ptr) -{ - struct bin_handle *binh = ptr; - - if (binh) { - if (binh->op && binh->h) - binh->op->close(binh->h); - file_put(binh->f); - } - free(binh); -} - -static TEE_Result system_open_ta_binary(struct system_ctx *ctx, - uint32_t param_types, - TEE_Param params[TEE_NUM_PARAMS]) -{ - TEE_Result res = TEE_SUCCESS; - struct bin_handle *binh = NULL; - int h = 0; - TEE_UUID *uuid = NULL; - uint8_t tag[FILE_TAG_SIZE] = { 0 }; - unsigned int tag_len = sizeof(tag); - uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INPUT, - TEE_PARAM_TYPE_VALUE_OUTPUT, - TEE_PARAM_TYPE_NONE, - TEE_PARAM_TYPE_NONE); - - if (exp_pt != param_types) - return TEE_ERROR_BAD_PARAMETERS; - if (params[0].memref.size != sizeof(*uuid)) - return TEE_ERROR_BAD_PARAMETERS; - - uuid = params[0].memref.buffer; - - binh = calloc(1, sizeof(*binh)); - if (!binh) - return TEE_ERROR_OUT_OF_MEMORY; - - SCATTERED_ARRAY_FOREACH(binh->op, ta_stores, struct ts_store_ops) { - DMSG("Lookup user TA ELF %pUl (%s)", - (void *)uuid, binh->op->description); - - res = binh->op->open(uuid, &binh->h); - DMSG("res=0x%x", res); - if (res != TEE_ERROR_ITEM_NOT_FOUND && - res != TEE_ERROR_STORAGE_NOT_AVAILABLE) - break; - } - if (res) - goto err; - - res = binh->op->get_size(binh->h, &binh->size_bytes); - if (res) - goto err; - res = binh->op->get_tag(binh->h, tag, &tag_len); - if (res) - goto err; - binh->f = file_get_by_tag(tag, tag_len); - if (!binh->f) - goto err_oom; - - h = handle_get(&ctx->db, binh); - if (h < 0) - goto err_oom; - params[0].value.a = h; - - return TEE_SUCCESS; -err_oom: - res = TEE_ERROR_OUT_OF_MEMORY; -err: - ta_bin_close(binh); - return res; -} - -static TEE_Result system_close_ta_binary(struct system_ctx *ctx, - uint32_t param_types, - TEE_Param params[TEE_NUM_PARAMS]) -{ - TEE_Result res = TEE_SUCCESS; - struct bin_handle *binh = NULL; - uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT, - TEE_PARAM_TYPE_NONE, - TEE_PARAM_TYPE_NONE, - TEE_PARAM_TYPE_NONE); - - if (exp_pt != param_types) - return TEE_ERROR_BAD_PARAMETERS; - - if (params[0].value.b) - return TEE_ERROR_BAD_PARAMETERS; - - binh = handle_put(&ctx->db, params[0].value.a); - if (!binh) - return TEE_ERROR_BAD_PARAMETERS; - - if (binh->offs_bytes < binh->size_bytes) - res = binh->op->read(binh->h, NULL, - binh->size_bytes - binh->offs_bytes); - - ta_bin_close(binh); - return res; -} - -static TEE_Result binh_copy_to(struct bin_handle *binh, vaddr_t va, - size_t offs_bytes, size_t num_bytes) -{ - TEE_Result res = TEE_SUCCESS; - size_t next_offs = 0; - - if (offs_bytes < binh->offs_bytes) - return TEE_ERROR_BAD_STATE; - - if (ADD_OVERFLOW(offs_bytes, num_bytes, &next_offs)) - return TEE_ERROR_BAD_PARAMETERS; - - if (offs_bytes > binh->offs_bytes) { - res = binh->op->read(binh->h, NULL, - offs_bytes - binh->offs_bytes); - if (res) - return res; - binh->offs_bytes = offs_bytes; - } - - if (next_offs > binh->size_bytes) { - size_t rb = binh->size_bytes - binh->offs_bytes; - - res = binh->op->read(binh->h, (void *)va, rb); - if (res) - return res; - memset((uint8_t *)va + rb, 0, num_bytes - rb); - binh->offs_bytes = binh->size_bytes; - } else { - res = binh->op->read(binh->h, (void *)va, num_bytes); - if (res) - return res; - binh->offs_bytes = next_offs; - } - - return TEE_SUCCESS; -} - -static TEE_Result system_map_ta_binary(struct system_ctx *ctx, - struct user_mode_ctx *uctx, - uint32_t param_types, - TEE_Param params[TEE_NUM_PARAMS]) -{ - const uint32_t accept_flags = PTA_SYSTEM_MAP_FLAG_SHAREABLE | - PTA_SYSTEM_MAP_FLAG_WRITEABLE | - PTA_SYSTEM_MAP_FLAG_EXECUTABLE; - uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT, - TEE_PARAM_TYPE_VALUE_INPUT, - TEE_PARAM_TYPE_VALUE_INOUT, - TEE_PARAM_TYPE_VALUE_INPUT); - struct bin_handle *binh = NULL; - uint32_t num_rounded_bytes = 0; - TEE_Result res = TEE_SUCCESS; - struct file_slice *fs = NULL; - bool file_is_locked = false; - struct mobj *mobj = NULL; - uint32_t offs_bytes = 0; - uint32_t offs_pages = 0; - uint32_t num_bytes = 0; - uint32_t pad_begin = 0; - uint32_t pad_end = 0; - size_t num_pages = 0; - uint32_t flags = 0; - uint32_t prot = 0; - vaddr_t va = 0; - - if (exp_pt != param_types) - return TEE_ERROR_BAD_PARAMETERS; - - binh = handle_lookup(&ctx->db, params[0].value.a); - if (!binh) - return TEE_ERROR_BAD_PARAMETERS; - flags = params[0].value.b; - offs_bytes = params[1].value.a; - num_bytes = params[1].value.b; - va = reg_pair_to_64(params[2].value.a, params[2].value.b); - pad_begin = params[3].value.a; - pad_end = params[3].value.b; - - if ((flags & accept_flags) != flags) - return TEE_ERROR_BAD_PARAMETERS; - - if ((flags & PTA_SYSTEM_MAP_FLAG_SHAREABLE) && - (flags & PTA_SYSTEM_MAP_FLAG_WRITEABLE)) - return TEE_ERROR_BAD_PARAMETERS; - - if ((flags & PTA_SYSTEM_MAP_FLAG_EXECUTABLE) && - (flags & PTA_SYSTEM_MAP_FLAG_WRITEABLE)) - return TEE_ERROR_BAD_PARAMETERS; - - if (offs_bytes & SMALL_PAGE_MASK) - return TEE_ERROR_BAD_PARAMETERS; - - prot = TEE_MATTR_UR | TEE_MATTR_PR; - if (flags & PTA_SYSTEM_MAP_FLAG_WRITEABLE) - prot |= TEE_MATTR_UW | TEE_MATTR_PW; - if (flags & PTA_SYSTEM_MAP_FLAG_EXECUTABLE) - prot |= TEE_MATTR_UX; - - offs_pages = offs_bytes >> SMALL_PAGE_SHIFT; - if (ROUNDUP_OVERFLOW(num_bytes, SMALL_PAGE_SIZE, &num_rounded_bytes)) - return TEE_ERROR_BAD_PARAMETERS; - num_pages = num_rounded_bytes / SMALL_PAGE_SIZE; - - if (!file_trylock(binh->f)) { - /* - * Before we can block on the file lock we must make all - * our page tables available for reclaiming in order to - * avoid a dead-lock with the other thread (which already - * is holding the file lock) mapping lots of memory below. - */ - vm_set_ctx(NULL); - file_lock(binh->f); - vm_set_ctx(uctx->ts_ctx); - } - file_is_locked = true; - fs = file_find_slice(binh->f, offs_pages); - if (fs) { - /* If there's registered slice it has to match */ - if (fs->page_offset != offs_pages || - num_pages > fs->fobj->num_pages) { - res = TEE_ERROR_BAD_PARAMETERS; - goto err; - } - - /* If there's a slice we must be mapping shareable */ - if (!(flags & PTA_SYSTEM_MAP_FLAG_SHAREABLE)) { - res = TEE_ERROR_BAD_PARAMETERS; - goto err; - } - - mobj = mobj_with_fobj_alloc(fs->fobj, binh->f); - if (!mobj) { - res = TEE_ERROR_OUT_OF_MEMORY; - goto err; - } - res = vm_map_pad(uctx, &va, num_rounded_bytes, - prot, VM_FLAG_READONLY, - mobj, 0, pad_begin, pad_end, 0); - mobj_put(mobj); - if (res) - goto err; - } else { - struct fobj *f = fobj_ta_mem_alloc(num_pages); - struct file *file = NULL; - uint32_t vm_flags = 0; - - if (!f) { - res = TEE_ERROR_OUT_OF_MEMORY; - goto err; - } - if (!(flags & PTA_SYSTEM_MAP_FLAG_WRITEABLE)) { - file = binh->f; - vm_flags |= VM_FLAG_READONLY; - } - - mobj = mobj_with_fobj_alloc(f, file); - fobj_put(f); - if (!mobj) { - res = TEE_ERROR_OUT_OF_MEMORY; - goto err; - } - res = vm_map_pad(uctx, &va, num_rounded_bytes, - TEE_MATTR_PRW, vm_flags, mobj, 0, - pad_begin, pad_end, 0); - mobj_put(mobj); - if (res) - goto err; - res = binh_copy_to(binh, va, offs_bytes, num_bytes); - if (res) - goto err_unmap_va; - res = vm_set_prot(uctx, va, num_rounded_bytes, - prot); - if (res) - goto err_unmap_va; - - /* - * The context currently is active set it again to update - * the mapping. - */ - vm_set_ctx(uctx->ts_ctx); - - if (!(flags & PTA_SYSTEM_MAP_FLAG_WRITEABLE)) { - res = file_add_slice(binh->f, f, offs_pages); - if (res) - goto err_unmap_va; - } - } - - file_unlock(binh->f); - - reg_pair_from_64(va, ¶ms[2].value.a, ¶ms[2].value.b); - return TEE_SUCCESS; - -err_unmap_va: - if (vm_unmap(uctx, va, num_rounded_bytes)) - panic(); - - /* - * The context currently is active set it again to update - * the mapping. - */ - vm_set_ctx(uctx->ts_ctx); - -err: - if (file_is_locked) - file_unlock(binh->f); - - return res; -} - -static TEE_Result system_copy_from_ta_binary(struct system_ctx *ctx, - uint32_t param_types, - TEE_Param params[TEE_NUM_PARAMS]) -{ - struct bin_handle *binh = NULL; - uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT, - TEE_PARAM_TYPE_MEMREF_OUTPUT, - TEE_PARAM_TYPE_NONE, - TEE_PARAM_TYPE_NONE); - - if (exp_pt != param_types) - return TEE_ERROR_BAD_PARAMETERS; - - binh = handle_lookup(&ctx->db, params[0].value.a); - if (!binh) - return TEE_ERROR_BAD_PARAMETERS; - - return binh_copy_to(binh, (vaddr_t)params[1].memref.buffer, - params[0].value.b, params[1].memref.size); -} - -static TEE_Result system_set_prot(struct user_mode_ctx *uctx, - uint32_t param_types, - TEE_Param params[TEE_NUM_PARAMS]) -{ - const uint32_t accept_flags = PTA_SYSTEM_MAP_FLAG_WRITEABLE | - PTA_SYSTEM_MAP_FLAG_EXECUTABLE; - uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT, - TEE_PARAM_TYPE_VALUE_INPUT, - TEE_PARAM_TYPE_NONE, - TEE_PARAM_TYPE_NONE); - uint32_t prot = TEE_MATTR_UR | TEE_MATTR_PR; - TEE_Result res = TEE_SUCCESS; - uint32_t vm_flags = 0; - uint32_t flags = 0; - vaddr_t end_va = 0; - vaddr_t va = 0; - size_t sz = 0; - - if (exp_pt != param_types) - return TEE_ERROR_BAD_PARAMETERS; - - flags = params[0].value.b; - - if ((flags & accept_flags) != flags) - return TEE_ERROR_BAD_PARAMETERS; - if (flags & PTA_SYSTEM_MAP_FLAG_WRITEABLE) - prot |= TEE_MATTR_UW | TEE_MATTR_PW; - if (flags & PTA_SYSTEM_MAP_FLAG_EXECUTABLE) - prot |= TEE_MATTR_UX; - - va = reg_pair_to_64(params[1].value.a, params[1].value.b); - sz = ROUNDUP(params[0].value.a, SMALL_PAGE_SIZE); - - /* - * The vm_get_flags() and vm_set_prot() are supposed to detect or - * handle overflow directly or indirectly. However, this function - * an API function so an extra guard here is in order. If nothing - * else to make it easier to review the code. - */ - if (ADD_OVERFLOW(va, sz, &end_va)) - return TEE_ERROR_BAD_PARAMETERS; - - res = vm_get_flags(uctx, va, sz, &vm_flags); - if (res) - return res; - if (vm_flags & VM_FLAG_PERMANENT) - return TEE_ERROR_ACCESS_DENIED; - - /* - * If the segment is a mapping of a part of a file (vm_flags & - * VM_FLAG_READONLY) it cannot be made writeable as all mapped - * files are mapped read-only. - */ - if ((vm_flags & VM_FLAG_READONLY) && - (prot & (TEE_MATTR_UW | TEE_MATTR_PW))) - return TEE_ERROR_ACCESS_DENIED; - - return vm_set_prot(uctx, va, sz, prot); -} - -static TEE_Result system_remap(struct user_mode_ctx *uctx, uint32_t param_types, - TEE_Param params[TEE_NUM_PARAMS]) -{ - uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT, - TEE_PARAM_TYPE_VALUE_INPUT, - TEE_PARAM_TYPE_VALUE_INOUT, - TEE_PARAM_TYPE_VALUE_INPUT); - TEE_Result res = TEE_SUCCESS; - uint32_t num_bytes = 0; - uint32_t pad_begin = 0; - uint32_t vm_flags = 0; - uint32_t pad_end = 0; - vaddr_t old_va = 0; - vaddr_t new_va = 0; - - if (exp_pt != param_types) - return TEE_ERROR_BAD_PARAMETERS; - - num_bytes = params[0].value.a; - old_va = reg_pair_to_64(params[1].value.a, params[1].value.b); - new_va = reg_pair_to_64(params[2].value.a, params[2].value.b); - pad_begin = params[3].value.a; - pad_end = params[3].value.b; - - res = vm_get_flags(uctx, old_va, num_bytes, &vm_flags); - if (res) - return res; - if (vm_flags & VM_FLAG_PERMANENT) - return TEE_ERROR_ACCESS_DENIED; - - res = vm_remap(uctx, &new_va, old_va, num_bytes, pad_begin, - pad_end); - if (!res) - reg_pair_from_64(new_va, ¶ms[2].value.a, - ¶ms[2].value.b); - - return res; -} - -/* ldelf has the same architecture/register width as the kernel */ -#ifdef ARM32 -static const bool is_arm32 = true; -#else -static const bool is_arm32; -#endif - -static TEE_Result call_ldelf_dlopen(struct user_mode_ctx *uctx, TEE_UUID *uuid, - uint32_t flags) -{ - uaddr_t usr_stack = uctx->ldelf_stack_ptr; - TEE_Result res = TEE_ERROR_GENERIC; - struct dl_entry_arg *arg = NULL; - uint32_t panic_code = 0; - uint32_t panicked = 0; - - assert(uuid); - - usr_stack -= ROUNDUP(sizeof(*arg), STACK_ALIGNMENT); - arg = (struct dl_entry_arg *)usr_stack; - - res = vm_check_access_rights(uctx, - TEE_MEMORY_ACCESS_READ | - TEE_MEMORY_ACCESS_WRITE | - TEE_MEMORY_ACCESS_ANY_OWNER, - (uaddr_t)arg, sizeof(*arg)); - if (res) { - EMSG("ldelf stack is inaccessible!"); - return res; - } - - memset(arg, 0, sizeof(*arg)); - arg->cmd = LDELF_DL_ENTRY_DLOPEN; - arg->dlopen.uuid = *uuid; - arg->dlopen.flags = flags; - - res = thread_enter_user_mode((vaddr_t)arg, 0, 0, 0, - usr_stack, uctx->dl_entry_func, - is_arm32, &panicked, &panic_code); - if (panicked) { - EMSG("ldelf dl_entry function panicked"); - abort_print_current_ta(); - res = TEE_ERROR_TARGET_DEAD; - } - if (!res) - res = arg->ret; - - return res; -} - -static TEE_Result call_ldelf_dlsym(struct user_mode_ctx *uctx, TEE_UUID *uuid, - const char *sym, size_t maxlen, vaddr_t *val) -{ - uaddr_t usr_stack = uctx->ldelf_stack_ptr; - TEE_Result res = TEE_ERROR_GENERIC; - struct dl_entry_arg *arg = NULL; - uint32_t panic_code = 0; - uint32_t panicked = 0; - size_t len = strnlen(sym, maxlen); - - if (len == maxlen) - return TEE_ERROR_BAD_PARAMETERS; - - usr_stack -= ROUNDUP(sizeof(*arg) + len + 1, STACK_ALIGNMENT); - arg = (struct dl_entry_arg *)usr_stack; - - res = vm_check_access_rights(uctx, - TEE_MEMORY_ACCESS_READ | - TEE_MEMORY_ACCESS_WRITE | - TEE_MEMORY_ACCESS_ANY_OWNER, - (uaddr_t)arg, sizeof(*arg) + len + 1); - if (res) { - EMSG("ldelf stack is inaccessible!"); - return res; - } - - memset(arg, 0, sizeof(*arg)); - arg->cmd = LDELF_DL_ENTRY_DLSYM; - arg->dlsym.uuid = *uuid; - memcpy(arg->dlsym.symbol, sym, len); - arg->dlsym.symbol[len] = '\0'; - - res = thread_enter_user_mode((vaddr_t)arg, 0, 0, 0, - usr_stack, uctx->dl_entry_func, - is_arm32, &panicked, &panic_code); - if (panicked) { - EMSG("ldelf dl_entry function panicked"); - abort_print_current_ta(); - res = TEE_ERROR_TARGET_DEAD; - } - if (!res) { - res = arg->ret; - if (!res) - *val = arg->dlsym.val; - } - - return res; -} - static TEE_Result system_dlopen(struct user_mode_ctx *uctx, uint32_t param_types, TEE_Param params[TEE_NUM_PARAMS]) @@ -766,7 +222,7 @@ static TEE_Result system_dlopen(struct user_mode_ctx *uctx, flags = params[1].value.a; s = ts_pop_current_session(); - res = call_ldelf_dlopen(uctx, uuid, flags); + res = ldelf_dlopen(uctx, uuid, flags); ts_push_current_session(s); return res; @@ -799,7 +255,7 @@ static TEE_Result system_dlsym(struct user_mode_ctx *uctx, uint32_t param_types, maxlen = params[1].memref.size; s = ts_pop_current_session(); - res = call_ldelf_dlsym(uctx, uuid, sym, maxlen, &va); + res = ldelf_dlsym(uctx, uuid, sym, maxlen, &va); ts_push_current_session(s); if (!res) @@ -876,19 +332,18 @@ static TEE_Result invoke_command(void *sess_ctx, uint32_t cmd_id, case PTA_SYSTEM_UNMAP: return system_unmap(uctx, param_types, params); case PTA_SYSTEM_OPEN_TA_BINARY: - return system_open_ta_binary(sess_ctx, param_types, params); + return ldelf_open_ta_binary(sess_ctx, param_types, params); case PTA_SYSTEM_CLOSE_TA_BINARY: - return system_close_ta_binary(sess_ctx, param_types, params); + return ldelf_close_ta_binary(sess_ctx, param_types, params); case PTA_SYSTEM_MAP_TA_BINARY: - return system_map_ta_binary(sess_ctx, uctx, param_types, - params); + return ldelf_map_ta_binary(sess_ctx, uctx, param_types, params); case PTA_SYSTEM_COPY_FROM_TA_BINARY: - return system_copy_from_ta_binary(sess_ctx, param_types, + return ldelf_copy_from_ta_binary(sess_ctx, param_types, params); case PTA_SYSTEM_SET_PROT: - return system_set_prot(uctx, param_types, params); + return ldelf_set_prot(uctx, param_types, params); case PTA_SYSTEM_REMAP: - return system_remap(uctx, param_types, params); + return ldelf_remap(uctx, param_types, params); case PTA_SYSTEM_DLOPEN: return system_dlopen(uctx, param_types, params); case PTA_SYSTEM_DLSYM: |