refactor(core): improve app_loader error handling
What changed, and why it matters
This commit is a code-quality refactor in Trezor's firmware that changes how the app loader reports failures. It replaces simple true/false return values with detailed error codes, adds cleanup paths, and makes the kernel halt if app-cache or app-loader initialization fails. The changes do not appear to fix a known exploitable bug, but they harden error handling and could prevent subtle failures from being silently ignored.
Treat as a defensive hardening commit. Review that `ts_code()` and `ts_make()` conversions in syscall dispatch/stubs correctly preserve error semantics and do not accidentally collapse distinct errors or lose the `__wur` warnings. Verify that `ensure_ok()` behavior on failure is acceptable for production boot flow.
Security signals we found
Kernel now panics (ensure_ok) if app_cache_init or app_loader_init fails, preventing continued boot with an uninitialized app loader
Error codes replace boolean returns, improving diagnosability and reducing silent failure modes
Cleanup paths in app_task_spawn and elf_load now consistently free resources on failure
app_cache_load_file emulator path now finalizes or discards the image explicitly
No changelog entry suggests this is treated as an internal refactor, not a user-visible security fix
Evidence from the diff
The commit refactors the app_loader subsystem to use the ts_t typed status/error-code system instead of bool. Functions such as app_arena_init, app_cache_init, app_cache_write_image, app_cache_finalize_image, app_loader_init, app_task_spawn, app_task_get_pminfo, and elf_load now return ts_t and use TSH_DECLARE/TSH_CHECK/TSH_RETURN macros for centralized error propagation. The STM32 and Unix ELF loaders, syscall dispatch/stubs/verifiers, and MicroPython bindings are updated to match. In drivers_init(), app_cache_init() and app_loader_init() results are now passed to ensure_ok(), causing a kernel panic on failure. Several cleanup paths are consolidated, and an emulator-only app_cache_load_file now explicitly finalizes the image on success and discards it on failure.
Changed components
core/embed/io/app_loader/app_arena.ccore/embed/io/app_loader/app_cache.ccore/embed/io/app_loader/app_task.ccore/embed/io/app_loader/stm32/elf_loader.ccore/embed/io/app_loader/unix/elf_loader.ccore/embed/sys/syscall/stm32/syscall_dispatch.ccore/embed/sys/syscall/stm32/syscall_stubs.ccore/embed/sys/syscall/stm32/syscall_verifiers.ccore/embed/projects/kernel/main.ccore/embed/upymod/modtrezorapp/modtrezorapp.ccore/embed/upymod/modtrezorapp/modtrezorapp-image.hInspect captured patch +266 / −234
diff --git a/core/embed/io/app_loader/app_arena.c b/core/embed/io/app_loader/app_arena.c
index 65fdda47..0603030d 100644
--- a/core/embed/io/app_loader/app_arena.c
+++ b/core/embed/io/app_loader/app_arena.c
@@ -58,24 +58,25 @@ typedef struct {
// Global app arena instance
static app_arena_t g_app_arena = {0};
-bool app_arena_init() {
+ts_t app_arena_init() {
app_arena_t* arena = &g_app_arena;
if (arena->initialized) {
- return true;
+ return TS_OK;
}
+ TSH_DECLARE;
+
memset(arena, 0, sizeof(app_arena_t));
#ifdef TREZOR_EMULATOR
arena->mem_size = 64 * 1024 * 1024;
arena->mem_ptr = malloc(arena->mem_size);
- if (arena->mem_ptr == NULL) {
- return false;
- }
+ TSH_CHECK(arena->mem_ptr != NULL, TS_ENOMEM);
#else
arena->mem_size = APPDATA_RAM_SIZE;
arena->mem_ptr = (uint8_t*)APPDATA_RAM_START;
+ TSH_CHECK(arena->mem_ptr != NULL, TS_ENOMEM);
#ifdef USE_TRUSTZONE
// Allow unprivileged access to app arena memory
@@ -87,7 +88,9 @@ bool app_arena_init() {
#endif
arena->initialized = true;
- return true;
+
+cleanup:
+ TSH_RETURN;
}
void* app_arena_alloc(size_t block_size, app_alloc_type_t type) {
diff --git a/core/embed/io/app_loader/app_arena.h b/core/embed/io/app_loader/app_arena.h
index 9a6b7f19..4a7d5668 100644
--- a/core/embed/io/app_loader/app_arena.h
+++ b/core/embed/io/app_loader/app_arena.h
@@ -19,6 +19,8 @@
#pragma once
+#include <trezor_types.h>
+
typedef enum {
APP_ALLOC_IMAGE, /** Image memory allocation */
APP_ALLOC_DATA, /** Data memory allocation */
@@ -26,9 +28,9 @@ typedef enum {
/** Initializes the application arena.
*
- * @return true on success, false on failure.
+ * @return TS_OK on success, or an error code on failure.
*/
-bool app_arena_init(void);
+ts_t __wur app_arena_init(void);
/**
* Allocates memory for an application.
@@ -42,6 +44,6 @@ void* app_arena_alloc(size_t size, app_alloc_type_t type);
/**
* Frees memory previously allocated with app_arena_alloc().
*
- * @param ptr Pointer to the memory to free.
+ * @param ptr Pointer to the memory to free. If NULL, no action is taken.
*/
void app_arena_free(void* ptr);
diff --git a/core/embed/io/app_loader/app_cache.c b/core/embed/io/app_loader/app_cache.c
index c20421e6..749d9729 100644
--- a/core/embed/io/app_loader/app_cache.c
+++ b/core/embed/io/app_loader/app_cache.c
@@ -51,21 +51,25 @@ typedef struct {
// Global app cache instance
static app_cache_t g_app_cache;
-bool app_cache_init(void) {
+ts_t app_cache_init(void) {
app_cache_t* cache = &g_app_cache;
if (cache->initialized) {
- return true;
+ return TS_OK;
}
- if (!app_arena_init()) {
- return false;
- }
+ TSH_DECLARE;
+ ts_t status;
memset(cache, 0, sizeof(*cache));
+ status = app_arena_init();
+ TSH_CHECK_OK(status);
+
cache->initialized = true;
- return true;
+
+cleanup:
+ TSH_RETURN;
}
static app_cache_image_t* find_entry_by_hash(const app_hash_t* hash) {
@@ -176,51 +180,46 @@ app_cache_handle_t app_cache_create_image(const app_hash_t* hash, size_t size) {
return image_to_handle(image);
}
-bool app_cache_write_image(app_cache_handle_t handle, uintptr_t offset,
+ts_t app_cache_write_image(app_cache_handle_t handle, uintptr_t offset,
const void* data, size_t size) {
app_cache_t* cache = &g_app_cache;
- if (!cache->initialized) {
- return false;
- }
+ TSH_DECLARE;
+
+ TSH_CHECK(cache->initialized, TS_ENOINIT);
app_cache_image_t* image = validate_image_handle(handle);
// Check whether the image exists and can be written to
- if (image == NULL || !image->loading) {
- return false;
- }
+ TSH_CHECK(image != NULL, TS_ENOENT);
+ TSH_CHECK(image->loading, TS_EBUSY);
+
+ // Check whether the image data is allocated
+ TSH_CHECK(image->image_data != NULL, TS_EINVAL);
// Check whether the offset and size are within bounds
- if (image->image_data == NULL || offset >= image->image_size ||
- size > image->image_size - offset) {
- return false;
- }
+ TSH_CHECK(offset < image->image_size, TS_EINVAL);
+ TSH_CHECK(size <= image->image_size - offset, TS_EINVAL);
// TODO: Consider a special new mpu mode or reusing MPU_MODE_APP here
mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_DISABLED);
memcpy((uint8_t*)image->image_data + offset, data, size);
mpu_restore(mpu_mode);
- return true;
+cleanup:
+ TSH_RETURN;
}
-bool app_cache_finalize_image(app_cache_handle_t handle, bool accept) {
+ts_t app_cache_finalize_image(app_cache_handle_t handle, bool accept) {
app_cache_t* cache = &g_app_cache;
- if (!cache->initialized) {
- return false;
- }
-
- app_cache_image_t* image = validate_image_handle(handle);
+ TSH_DECLARE;
- if (image == NULL) {
- return false;
- }
+ TSH_CHECK(cache->initialized, TS_ENOINIT);
- if (!image->loading) {
- return false;
- }
+ app_cache_image_t* image = validate_image_handle(handle);
+ TSH_CHECK(image != NULL, TS_ENOENT);
+ TSH_CHECK(image->loading, TS_EINVAL);
if (accept) {
image->loading = false;
@@ -228,7 +227,8 @@ bool app_cache_finalize_image(app_cache_handle_t handle, bool accept) {
remove_entry(image);
}
- return true;
+cleanup:
+ TSH_RETURN;
}
app_cache_handle_t app_cache_lock_image(const app_hash_t* hash, void** ptr,
@@ -269,43 +269,45 @@ void app_cache_unlock_image(app_cache_handle_t handle) {
}
#ifdef TREZOR_EMULATOR
-bool app_cache_load_file(const app_hash_t* hash, const char* filename) {
- bool retval = false;
+ts_t app_cache_load_file(const app_hash_t* hash, const char* filename) {
+ TSH_DECLARE;
+ ts_t status;
app_cache_handle_t image = APP_CACHE_INVALID_HANDLE;
FILE* f = fopen(filename, "rb");
- if (f == NULL) {
- goto cleanup;
- }
+ TSH_CHECK(f != NULL, TS_EIO);
fseek(f, 0, SEEK_END);
size_t size = ftell(f);
fseek(f, 0, SEEK_SET);
image = app_cache_create_image(hash, size);
-
- if (image == APP_CACHE_INVALID_HANDLE) {
- goto cleanup;
- }
+ TSH_CHECK(image != APP_CACHE_INVALID_HANDLE, TS_ENOMEM);
uintptr_t offset = 0;
while (size > 0) {
uint8_t buffer[1024];
size_t to_read = size < sizeof(buffer) ? size : sizeof(buffer);
+
size_t read = fread(buffer, 1, to_read, f);
- if (read != to_read) {
- goto cleanup;
- }
- if (!app_cache_write_image(image, offset, buffer, read)) {
- goto cleanup;
- }
+ TSH_CHECK(read == to_read, TS_EIO);
+
+ status = app_cache_write_image(image, offset, buffer, read);
+ TSH_CHECK_OK(status);
+
offset += read;
size -= read;
}
- retval = true;
+ fclose(f);
+ f = NULL;
+
+ status = app_cache_finalize_image(image, true);
+ TSH_CHECK_OK(status);
+
+ image = APP_CACHE_INVALID_HANDLE;
cleanup:
if (f != NULL) {
@@ -313,10 +315,11 @@ cleanup:
}
if (image != APP_CACHE_INVALID_HANDLE) {
- app_cache_finalize_image(image, retval);
+ status = app_cache_finalize_image(image, false);
+ UNUSED(status);
}
- return retval;
+ TSH_RETURN;
}
#endif
diff --git a/core/embed/io/app_loader/app_task.c b/core/embed/io/app_loader/app_task.c
index 7dba4bbc..ef9bfc86 100644
--- a/core/embed/io/app_loader/app_task.c
+++ b/core/embed/io/app_loader/app_task.c
@@ -52,21 +52,25 @@ typedef struct {
// Global app loader instance
static app_loader_t g_app_loader;
-bool app_loader_init(void) {
+ts_t app_loader_init(void) {
app_loader_t* loader = &g_app_loader;
if (loader->initialized) {
- return true;
+ return TS_OK;
}
- if (!app_arena_init()) {
- return false;
- }
+ TSH_DECLARE;
+ ts_t status;
memset(loader, 0, sizeof(*loader));
+ status = app_arena_init();
+ TSH_CHECK_OK(status);
+
loader->initialized = true;
- return true;
+
+cleanup:
+ TSH_RETURN;
}
static app_entry_t* find_app_by_task(systask_id_t task_id) {
@@ -121,45 +125,50 @@ static void remove_entry(app_entry_t* entry) {
memset(entry, 0, sizeof(*entry));
}
-bool app_task_spawn(const app_hash_t* hash, systask_id_t* task_id) {
+ts_t app_task_spawn(const app_hash_t* hash, systask_id_t* task_id) {
app_loader_t* loader = &g_app_loader;
- if (!loader->initialized) {
- return false;
- }
+ TSH_DECLARE;
+ ts_t status;
- app_entry_t* entry = find_app_by_hash(hash);
- if (entry != NULL) {
- // Application is already spawned
- return false;
- }
+ app_entry_t* entry = NULL;
+
+ TSH_CHECK(loader->initialized, TS_ENOINIT);
+
+ // Check if the application is already spawned
+ TSH_CHECK(find_app_by_hash(hash) == NULL, TS_EBUSY);
entry = alloc_entry(hash);
- if (entry == NULL) {
- // No space for new app entry
- return false;
- }
+ TSH_CHECK(entry != NULL, TS_ENOMEM); // No space for new app entry
void* image_ptr = NULL;
size_t image_size = 0;
entry->locked_image = app_cache_lock_image(hash, &image_ptr, &image_size);
- if (entry->locked_image == APP_CACHE_INVALID_HANDLE) {
- // Unable to lock application image in cache
- remove_entry(entry);
- return false;
- }
+ TSH_CHECK(entry->locked_image != APP_CACHE_INVALID_HANDLE, TS_ENOENT);
- if (!elf_load(&entry->applet, image_ptr, image_size)) {
- remove_entry(entry);
- return false;
+ status = elf_load(&entry->applet, image_ptr, image_size);
+
+ if (ts_error(status)) {
+ if (!ts_eq(status, TS_ENOMEM)) {
+ // Remap to generic error
+ status = TS_EINVAL;
+ }
}
+ TSH_CHECK_OK(status);
applet_run(&entry->applet);
*task_id = entry->applet.task.id;
- return true;
+ TSH_RETURN;
+
+cleanup:
+ if (entry != NULL) {
+ remove_entry(entry);
+ }
+
+ TSH_RETURN;
}
bool app_task_is_running(systask_id_t task_id) {
@@ -177,22 +186,22 @@ bool app_task_is_running(systask_id_t task_id) {
return systask_is_alive(&entry->applet.task);
}
-bool app_task_get_pminfo(systask_id_t task_id, systask_postmortem_t* pminfo) {
+ts_t app_task_get_pminfo(systask_id_t task_id, systask_postmortem_t* pminfo) {
app_loader_t* loader = &g_app_loader;
+ TSH_DECLARE;
+
memset(pminfo, 0, sizeof(*pminfo));
- if (!loader->initialized) {
- return false;
- }
+ TSH_CHECK(loader->initialized, TS_ENOINIT);
app_entry_t* entry = find_app_by_task(task_id);
- if (entry == NULL) {
- return false;
- }
+ TSH_CHECK(entry != NULL, TS_ENOENT);
*pminfo = entry->applet.task.pminfo;
- return true;
+
+cleanup:
+ TSH_RETURN;
}
void app_task_unload(systask_id_t task_id) {
diff --git a/core/embed/io/app_loader/inc/io/app_cache.h b/core/embed/io/app_loader/inc/io/app_cache.h
index cb302941..452f9a06 100644
--- a/core/embed/io/app_loader/inc/io/app_cache.h
+++ b/core/embed/io/app_loader/inc/io/app_cache.h
@@ -39,9 +39,9 @@ typedef uintptr_t app_cache_handle_t;
/**
* Initializes the app cache subsystem.
*
- * @return true on success, false on failure.
+ * @return TS_OK on success, error code on failure.
*/
-bool app_cache_init(void);
+ts_t __wur app_cache_init(void);
#endif
@@ -70,10 +70,10 @@ app_cache_handle_t app_cache_create_image(const app_hash_t* hash, size_t size);
* @param data Pointer to the data to write.
* @param size The size of the data to write.
*
- * @return true on success, false on failure.
+ * @return TS_OK on success, error code on failure.
*/
-bool app_cache_write_image(app_cache_handle_t handle, uintptr_t offset,
- const void* data, size_t size);
+ts_t __wur app_cache_write_image(app_cache_handle_t handle, uintptr_t offset,
+ const void* data, size_t size);
/**
* Finalizes loading of the application image. If `accept` is true,
@@ -83,10 +83,9 @@ bool app_cache_write_image(app_cache_handle_t handle, uintptr_t offset,
* @param handle The application image handle.
* @param accept If true, the image is marked as loaded; if false,
* the image is discarded.
- * @return true on success, false on failure.
+ * @return TS_OK on success, error code on failure.
*/
-
-bool app_cache_finalize_image(app_cache_handle_t handle, bool accept);
+ts_t __wur app_cache_finalize_image(app_cache_handle_t handle, bool accept);
#ifdef KERNEL_MODE
@@ -97,7 +96,7 @@ bool app_cache_finalize_image(app_cache_handle_t handle, bool accept);
* @param ptr Pointer to store the address of the application image.
* @param size Pointer to store the size of the application image.
*
- * @return true on success, false on failure.
+ * @return A handle to the locked application image.
*/
app_cache_handle_t app_cache_lock_image(const app_hash_t* hash, void** ptr,
@@ -122,8 +121,8 @@ void app_cache_unlock_image(app_cache_handle_t handle);
* @param hash The application hash.
* @param filename The path to the file containing the application image.
*
- * @return true on success, false on failure.
+ * @return TS_OK on success, error code on failure.
*/
-bool app_cache_load_file(const app_hash_t* hash, const char* filename);
+ts_t __wur app_cache_load_file(const app_hash_t* hash, const char* filename);
#endif // TREZOR_EMULATOR
diff --git a/core/embed/io/app_loader/inc/io/app_loader.h b/core/embed/io/app_loader/inc/io/app_loader.h
index 8dc39940..f577b6c9 100644
--- a/core/embed/io/app_loader/inc/io/app_loader.h
+++ b/core/embed/io/app_loader/inc/io/app_loader.h
@@ -30,9 +30,9 @@
/**
* Initializes the app loader module.
*
- * @return true on success, false on failure.
+ * @return TS_OK on success, or an error code on failure.
*/
-bool app_loader_init(void);
+ts_t __wur app_loader_init(void);
#endif
@@ -42,9 +42,12 @@ bool app_loader_init(void);
* @param hash Pointer to the application hash.
* @param task_id Pointer to store the spawned application's task ID.
*
- * @return true if the application was successfully spawned, false otherwise.
+ * @return TS_OK on success, or an error code on failure:
+ * TS_ENOENT if image not found, or an pother error
+ * TS_ENOMEM if there is not enough memory
+ * TS_EINVAL if the application image is invalid
*/
-bool app_task_spawn(const app_hash_t* hash, systask_id_t* task_id);
+ts_t __wur app_task_spawn(const app_hash_t* hash, systask_id_t* task_id);
/**
* Checks if an application is currently running.
@@ -58,9 +61,10 @@ bool app_task_is_running(systask_id_t task_id);
*
* @param task_id The system task identifier of the application.
* @param info Pointer to a structure to receive postmortem information.
- * @return true if postmortem information was retrieved, false otherwise.
+ * @return TS_OK on success, or an error code on failure.
*/
-bool app_task_get_pminfo(systask_id_t task_id, systask_postmortem_t* pminfo);
+ts_t __wur app_task_get_pminfo(systask_id_t task_id,
+ systask_postmortem_t* pminfo);
/**
* Unloads an application and frees all associated resources.
diff --git a/core/embed/io/app_loader/inc/io/elf_loader.h b/core/embed/io/app_loader/inc/io/elf_loader.h
index 0c6caa07..c5550b22 100644
--- a/core/embed/io/app_loader/inc/io/elf_loader.h
+++ b/core/embed/io/app_loader/inc/io/elf_loader.h
@@ -32,6 +32,9 @@
* @param applet Pointer to the applet_t structure to be initialized
* @param elf_ptr Pointer to the pointer to the ELF image loaded in memory
* @param elf_size Size of the ELF image in memory
- * @return true on success, false on failure
+ *
+ * @return TS_OK on success, or an error code on failure:
+ * TS_ENOMEM if there is not enough memory
+ * TS_EINVAL if the ELF image is invalid
*/
-bool elf_load(applet_t* applet, const void* elf_ptr, size_t elf_size);
+ts_t __wur elf_load(applet_t* applet, const void* elf_ptr, size_t elf_size);
diff --git a/core/embed/io/app_loader/stm32/elf_loader.c b/core/embed/io/app_loader/stm32/elf_loader.c
index 79588759..1c698b36 100644
--- a/core/embed/io/app_loader/stm32/elf_loader.c
+++ b/core/embed/io/app_loader/stm32/elf_loader.c
@@ -31,11 +31,14 @@
#include <sec/image.h>
#include <sys/applet.h>
#include <sys/coreapp.h>
+#include <sys/logging.h>
#include <sys/mpu.h>
#include "../app_arena.h"
#include "elf.h"
+LOG_DECLARE(elf_loader)
+
// Alignment required for MPU regions
#define MPU_ALIGNMENT 32
@@ -240,38 +243,33 @@ static Elf32_Addr map_va(va_mapping_t* map, Elf32_Addr va) {
return 0;
}
-static bool relocate_section(const Elf32_Ehdr* ehdr, const Elf32_Shdr* shdr,
+static ts_t relocate_section(const Elf32_Ehdr* ehdr, const Elf32_Shdr* shdr,
va_mapping_t* map) {
+ TSH_DECLARE;
+
const Elf32_Rel* rel = (Elf32_Rel*)((uint32_t)ehdr + shdr->sh_offset);
const Elf32_Rel* rel_end = (Elf32_Rel*)((uint32_t)rel + shdr->sh_size);
// Get section we are relocating
const Elf32_Shdr* target_shdr = elf_get_shdr(ehdr, shdr->sh_info);
- if (target_shdr == NULL) {
- return false;
- }
+
+ TSH_CHECK(target_shdr != NULL, TS_EINVAL);
// Get target section boundaries
uint32_t target_start = map_va(map, target_shdr->sh_addr);
uint32_t target_end = target_start + target_shdr->sh_size;
while (rel < rel_end) {
- if (ELF32_R_TYPE(rel->r_info) != R_ARM_ABS32) {
- // Unsupported relocation type
- return false;
- }
+ // Is relocation type supported?
+ TSH_CHECK(ELF32_R_TYPE(rel->r_info) == R_ARM_ABS32, TS_EINVAL);
// Get pointer to the relocated 32-bit word
uint32_t* mem_ptr = (uint32_t*)map_va(map, rel->r_offset);
- if (mem_ptr == NULL) {
- return false;
- }
+ TSH_CHECK(mem_ptr != NULL, TS_EINVAL);
// Ensure the pointer is within the target section
- if ((uint32_t)mem_ptr < target_start ||
- (uint32_t)mem_ptr + 4 > target_end) {
- return false;
- }
+ TSH_CHECK((uint32_t)mem_ptr >= target_start, TS_EINVAL);
+ TSH_CHECK((uint32_t)mem_ptr + 4 <= target_end, TS_EINVAL);
// Relocate the 32-bit word
*mem_ptr = map_va(map, *mem_ptr);
@@ -279,7 +277,8 @@ static bool relocate_section(const Elf32_Ehdr* ehdr, const Elf32_Shdr* shdr,
++rel;
}
- return true;
+cleanup:
+ TSH_RETURN;
}
static void get_stack_info(const Elf32_Ehdr* ehdr, uint32_t* stack_base,
@@ -311,8 +310,12 @@ static void elf_unload_cb(applet_t* applet) {
}
}
-bool elf_load(applet_t* applet, const void* elf_ptr, size_t elf_size) {
- bool retval = false;
+ts_t elf_load(applet_t* applet, const void* elf_ptr, size_t elf_size) {
+ TSH_DECLARE;
+ ts_t status;
+
+ void* ram_ptr = NULL;
+ size_t ram_size = 0;
applet_init(applet, NULL, NULL);
@@ -325,28 +328,20 @@ bool elf_load(applet_t* applet, const void* elf_ptr, size_t elf_size) {
// Read and validate ELF header
const Elf32_Ehdr* ehdr = elf_read_header(elf_ptr, elf_size);
- if (ehdr == NULL) {
- goto cleanup;
- }
+ TSH_CHECK(ehdr != NULL, TS_EINVAL);
// Read and validate RO segment
const Elf32_Phdr* ro_phdr = elf_read_ro_phdr(ehdr, elf_size);
- if (ro_phdr == NULL) {
- goto cleanup;
- }
+ TSH_CHECK(ro_phdr != NULL, TS_EINVAL);
// Read and validate RW segment
const Elf32_Phdr* rw_phdr = elf_read_rw_phdr(ehdr, elf_size);
- if (rw_phdr == NULL) {
- goto cleanup;
- }
+ TSH_CHECK(rw_phdr != NULL, TS_EINVAL);
// Allocate RAM for RW segment
- size_t ram_size = ALIGN_UP(rw_phdr->p_memsz, MPU_ALIGNMENT);
- void* ram_ptr = app_arena_alloc(ram_size, APP_ALLOC_DATA);
- if (ram_ptr == NULL) {
- goto cleanup;
- }
+ ram_size = ALIGN_UP(rw_phdr->p_memsz, MPU_ALIGNMENT);
+ ram_ptr = app_arena_alloc(ram_size, APP_ALLOC_DATA);
+ TSH_CHECK(ram_ptr != NULL, TS_ENOMEM);
// Make sure ELF and allocated RAM are accessible
// (temporarily map it as data => we can apply relocation fixups)
@@ -376,9 +371,8 @@ bool elf_load(applet_t* applet, const void* elf_ptr, size_t elf_size) {
for (int i = 0; i < ehdr->e_shnum; i++) {
const Elf32_Shdr* shdr = elf_get_shdr(ehdr, i);
if (shdr->sh_type == SHT_REL) {
- if (!relocate_section(ehdr, shdr, &map)) {
- goto cleanup;
- }
+ status = relocate_section(ehdr, shdr, &map);
+ TSH_CHECK_OK(status);
}
}
@@ -387,9 +381,7 @@ bool elf_load(applet_t* applet, const void* elf_ptr, size_t elf_size) {
uint32_t stack_size = 0;
get_stack_info(ehdr, &stack_base, &stack_size);
stack_base = map_va(&map, stack_base);
- if (stack_base == 0 || stack_size == 0) {
- goto cleanup;
- }
+ TSH_CHECK(stack_base != 0 && stack_size > 0, TS_EINVAL);
// Get static base address
uint32_t sb_addr = (uintptr_t)ram_ptr;
@@ -417,9 +409,9 @@ bool elf_load(applet_t* applet, const void* elf_ptr, size_t elf_size) {
mpu_set_active_applet(&applet->layout);
// Initialize the applet task
- if (!systask_init(&applet->task, stack_base, stack_size, sb_addr, applet)) {
- goto cleanup;
- }
+ bool ok =
+ systask_init(&applet->task, stack_base, stack_size, sb_addr, applet);
+ TSH_CHECK(ok, TS_ENOMEM);
// Enable coreapp TLS area swapping
systask_enable_tls(&applet->task, coreapp_get_tls_area());
@@ -428,26 +420,23 @@ bool elf_load(applet_t* applet, const void* elf_ptr, size_t elf_size) {
// Prepare the applet to run - push exception frame on the stack
// with the entrypoint address
- if (!systask_push_call(&applet->task, (void*)entrypoint, api_getter, 0, 0)) {
- goto cleanup;
- }
+ ok = systask_push_call(&applet->task, (void*)entrypoint, api_getter, 0, 0);
+ TSH_CHECK(ok, TS_ENOMEM);
- retval = true;
+ // Recover MPU state for the active task
+ systask_set_mpu(systask_active());
-cleanup:
+ TSH_RETURN;
- if (ram_ptr != NULL) {
- app_arena_free(ram_ptr);
- }
+cleanup:
- if (!retval) {
- applet_unload(applet);
- }
+ app_arena_free(ram_ptr);
+ applet_unload(applet);
// Recover MPU state for the active task
systask_set_mpu(systask_active());
- return retval;
+ TSH_RETURN;
}
#endif // KERNEL_MODE
diff --git a/core/embed/io/app_loader/unix/elf_loader.c b/core/embed/io/app_loader/unix/elf_loader.c
index b3ca223a..f09a2ebc 100644
--- a/core/embed/io/app_loader/unix/elf_loader.c
+++ b/core/embed/io/app_loader/unix/elf_loader.c
@@ -21,13 +21,12 @@
#include <io/elf_loader.h>
#include <sys/coreapp.h>
+#include <sys/logging.h>
#include <dlfcn.h>
#include <unistd.h>
-#ifdef USE_DBG_CONSOLE
-#include <sys/dbg_console.h>
-#endif
+LOG_DECLARE(elf_loader)
static void elf_applet_unload(applet_t* applet) {
if (applet->handle != NULL) {
@@ -36,21 +35,27 @@ static void elf_applet_unload(applet_t* applet) {
}
}
-bool write_to_file(const char* filename, const void* elf_ptr, size_t elf_size) {
- FILE* f = fopen(filename, "wb");
+ts_t write_to_file(const char* filename, const void* elf_ptr, size_t elf_size) {
+ TSH_DECLARE;
- if (f == NULL) {
- return false;
- }
+ FILE* f = fopen(filename, "wb");
+ TSH_CHECK(f != NULL, TS_EIO);
- int rc = fwrite(elf_ptr, 1, elf_size, f);
+ size_t rc = fwrite(elf_ptr, 1, elf_size, f);
+ TSH_CHECK(rc == elf_size, TS_EIO);
- fclose(f);
+cleanup:
+ if (f != NULL) {
+ fclose(f);
+ }
- return rc == elf_size;
+ TSH_RETURN;
}
-bool elf_load(applet_t* applet, const void* elf_ptr, size_t elf_size) {
+ts_t elf_load(applet_t* applet, const void* elf_ptr, size_t elf_size) {
+ TSH_DECLARE;
+ ts_t status;
+
applet_privileges_t privileges = {0};
applet_init(applet, &privileges, elf_applet_unload);
@@ -59,42 +64,31 @@ bool elf_load(applet_t* applet, const void* elf_ptr, size_t elf_size) {
// Copy the image to the temporary file that will be
// unlinked just after it's loaded
- if (!write_to_file(filename, elf_ptr, elf_size)) {
- goto cleanup;
- }
+ status = write_to_file(filename, elf_ptr, elf_size);
+ TSH_CHECK_OK(status);
applet->handle = dlopen(filename, RTLD_NOW);
-
unlink(filename);
-
if (applet->handle == NULL) {
-#ifdef USE_DBG_CONSOLE
- dbg_printf("elf_load: %s\n", dlerror());
-#endif
- // Failed to load the applet
- goto cleanup;
+ LOG_ERR("dlopen failed: %s", dlerror());
}
+ TSH_CHECK(applet->handle != NULL, TS_EINVAL);
void* entrypoint = dlsym(applet->handle, "applet_main");
+ TSH_CHECK(entrypoint != NULL, TS_EINVAL);
- if (entrypoint == NULL) {
- // Applet entry point not found
- goto cleanup;
- }
-
- if (!systask_init(&applet->task, 0, 0, 0, applet)) {
- goto cleanup;
- }
+ bool ok = systask_init(&applet->task, 0, 0, 0, applet);
+ TSH_CHECK(ok, TS_ENOMEM);
uintptr_t api_getter = (uintptr_t)coreapp_get_api_getter();
- if (!systask_push_call(&applet->task, entrypoint, api_getter, 0, 0)) {
- goto cleanup;
- }
+ ok = systask_push_call(&applet->task, entrypoint, api_getter, 0, 0);
+ TSH_CHECK(ok, TS_ENOMEM);
- return true;
+ TSH_RETURN;
cleanup:
applet_unload(applet);
- return false;
+
+ TSH_RETURN;
}
diff --git a/core/embed/projects/kernel/main.c b/core/embed/projects/kernel/main.c
index 67badea5..3a5532bb 100644
--- a/core/embed/projects/kernel/main.c
+++ b/core/embed/projects/kernel/main.c
@@ -109,6 +109,10 @@
#endif
void drivers_init() {
+ ts_t status;
+
+ UNUSED(status);
+
#ifdef SECURE_MODE
parse_boardloader_capabilities();
unit_properties_init();
@@ -175,7 +179,7 @@ void drivers_init() {
#endif
#ifdef USE_HAPTIC
- ts_t status = haptic_init();
+ status = haptic_init();
UNUSED(status);
#endif
@@ -197,8 +201,11 @@ void drivers_init() {
#endif
#ifdef USE_APP_LOADING
- app_cache_init();
- app_loader_init();
+ status = app_cache_init();
+ ensure_ok(status, "app_cache_init failed");
+
+ status = app_loader_init();
+ ensure_ok(status, "app_loader_init failed");
#endif
}
diff --git a/core/embed/sys/dbg/inc/sys/syslog_config.h b/core/embed/sys/dbg/inc/sys/syslog_config.h
index 7405ee0f..e0097e0e 100644
--- a/core/embed/sys/dbg/inc/sys/syslog_config.h
+++ b/core/embed/sys/dbg/inc/sys/syslog_config.h
@@ -60,6 +60,10 @@
#define SYSLOG_ble_driver_MAX_LOG_LEVEL SYSLOG_DEFAULT_LOG_LEVEL
#endif
+#ifndef SYSLOG_elf_loader_MAX_LOG_LEVEL
+#define SYSLOG_elf_loader_MAX_LOG_LEVEL SYSLOG_DEFAULT_LOG_LEVEL
+#endif
+
// Optiga command log is relatively quiet
#ifndef SYSLOG_optiga_MAX_LOG_LEVEL
#define SYSLOG_optiga_MAX_LOG_LEVEL SYSLOG_DEFAULT_LOG_LEVEL
diff --git a/core/embed/sys/syscall/stm32/syscall_dispatch.c b/core/embed/sys/syscall/stm32/syscall_dispatch.c
index 7c1951cb..f0cd39ab 100644
--- a/core/embed/sys/syscall/stm32/syscall_dispatch.c
+++ b/core/embed/sys/syscall/stm32/syscall_dispatch.c
@@ -940,7 +940,8 @@ __attribute((no_stack_protector)) void syscall_handler(uint32_t *args,
case SYSCALL_APP_TASK_SPAWN: {
const app_hash_t *hash = (const app_hash_t *)args[0];
systask_id_t *task_id = (systask_id_t *)args[1];
- args[0] = app_task_spawn__verified(hash, task_id);
+ ts_t status = app_task_spawn__verified(hash, task_id);
+ args[0] = ts_code(status);
} break;
case SYSCALL_APP_TASK_IS_RUNNING: {
@@ -951,7 +952,8 @@ __attribute((no_stack_protector)) void syscall_handler(uint32_t *args,
case SYSCALL_APP_TASK_GET_PMINFO: {
systask_id_t task_id = (systask_id_t)args[0];
systask_postmortem_t *pminfo = (systask_postmortem_t *)args[1];
- args[0] = app_task_get_pminfo__verified(task_id, pminfo);
+ ts_t status = app_task_get_pminfo__verified(task_id, pminfo);
+ args[0] = ts_code(status);
} break;
case SYSCALL_APP_TASK_UNLOAD: {
@@ -970,13 +972,15 @@ __attribute((no_stack_protector)) void syscall_handler(uint32_t *args,
uintptr_t offset = (uintptr_t)args[1];
const void *data = (const void *)args[2];
size_t size = (size_t)args[3];
- args[0] = app_cache_write_image__verified(handle, offset, data, size);
+ ts_t status = app_cache_write_image__verified(handle, offset, data, size);
+ args[0] = ts_code(status);
} break;
case SYSCALL_APP_CACHE_FINALIZE_IMAGE: {
app_cache_handle_t handle = (app_cache_handle_t)args[0];
bool accept = (bool)args[1];
- args[0] = app_cache_finalize_image(handle, accept);
+ ts_t status = app_cache_finalize_image(handle, accept);
+ args[0] = ts_code(status);
} break;
#endif
diff --git a/core/embed/sys/syscall/stm32/syscall_stubs.c b/core/embed/sys/syscall/stm32/syscall_stubs.c
index 0eebc107..a6cccbf2 100644
--- a/core/embed/sys/syscall/stm32/syscall_stubs.c
+++ b/core/embed/sys/syscall/stm32/syscall_stubs.c
@@ -937,18 +937,18 @@ bool tropic_data_read(uint16_t udata_slot, uint8_t *data, uint16_t *size) {
#include <io/app_loader.h>
-bool app_task_spawn(const app_hash_t *hash, systask_id_t *task_id) {
- return (bool)syscall_invoke2((uint32_t)hash, (uint32_t)task_id,
- SYSCALL_APP_TASK_SPAWN);
+ts_t app_task_spawn(const app_hash_t *hash, systask_id_t *task_id) {
+ return ts_make(syscall_invoke2((uint32_t)hash, (uint32_t)task_id,
+ SYSCALL_APP_TASK_SPAWN));
}
bool app_task_is_running(systask_id_t task_id) {
return (bool)syscall_invoke1((uint32_t)task_id, SYSCALL_APP_TASK_IS_RUNNING);
}
-bool app_task_get_pminfo(systask_id_t task_id, systask_postmortem_t *pminfo) {
- return (bool)syscall_invoke2((uint32_t)task_id, (uint32_t)pminfo,
- SYSCALL_APP_TASK_GET_PMINFO);
+ts_t app_task_get_pminfo(systask_id_t task_id, systask_postmortem_t *pminfo) {
+ return ts_make(syscall_invoke2((uint32_t)task_id, (uint32_t)pminfo,
+ SYSCALL_APP_TASK_GET_PMINFO));
}
void app_task_unload(systask_id_t task_id) {
@@ -960,16 +960,16 @@ app_cache_handle_t app_cache_create_image(const app_hash_t *hash, size_t size) {
SYSCALL_APP_CACHE_CREATE_IMAGE);
}
-bool app_cache_write_image(app_cache_handle_t handle, uintptr_t offset,
+ts_t app_cache_write_image(app_cache_handle_t handle, uintptr_t offset,
const void *data, size_t data_size) {
- return (bool)syscall_invoke4((uint32_t)handle, (uint32_t)offset,
- (uint32_t)data, data_size,
- SYSCALL_APP_CACHE_WRITE_IMAGE);
+ return ts_make(syscall_invoke4((uint32_t)handle, (uint32_t)offset,
+ (uint32_t)data, data_size,
+ SYSCALL_APP_CACHE_WRITE_IMAGE));
}
-bool app_cache_finalize_image(app_cache_handle_t handle, bool accept) {
- return (bool)syscall_invoke2((uint32_t)handle, (uint32_t)accept,
- SYSCALL_APP_CACHE_FINALIZE_IMAGE);
+ts_t app_cache_finalize_image(app_cache_handle_t handle, bool accept) {
+ return ts_make(syscall_invoke2((uint32_t)handle, (uint32_t)accept,
+ SYSCALL_APP_CACHE_FINALIZE_IMAGE));
}
#endif
diff --git a/core/embed/sys/syscall/stm32/syscall_verifiers.c b/core/embed/sys/syscall/stm32/syscall_verifiers.c
index 4db27545..0a25a347 100644
--- a/core/embed/sys/syscall/stm32/syscall_verifiers.c
+++ b/core/embed/sys/syscall/stm32/syscall_verifiers.c
@@ -1378,7 +1378,7 @@ access_violation:
#ifdef USE_APP_LOADING
-bool app_task_spawn__verified(const app_hash_t *hash, systask_id_t *task_id) {
+ts_t app_task_spawn__verified(const app_hash_t *hash, systask_id_t *task_id) {
if (!probe_read_access(hash, sizeof(*hash))) {
goto access_violation;
}
@@ -1390,10 +1390,10 @@ bool app_task_spawn__verified(const app_hash_t *hash, systask_id_t *task_id) {
return app_task_spawn(hash, task_id);
access_violation:
apptask_access_violation();
- return false;
+ return TS_EACCES;
}
-bool app_task_get_pminfo__verified(systask_id_t task_id,
+ts_t app_task_get_pminfo__verified(systask_id_t task_id,
systask_postmortem_t *pminfo) {
if (!probe_write_access(pminfo, sizeof(*pminfo))) {
goto access_violation;
@@ -1402,7 +1402,7 @@ bool app_task_get_pminfo__verified(systask_id_t task_id,
return app_task_get_pminfo(task_id, pminfo);
access_violation:
apptask_access_violation();
- return false;
+ return TS_EACCES;
}
app_cache_handle_t app_cache_create_image__verified(const app_hash_t *hash,
@@ -1418,7 +1418,7 @@ access_violation:
return APP_CACHE_INVALID_HANDLE;
}
-bool app_cache_write_image__verified(app_cache_handle_t handle,
+ts_t app_cache_write_image__verified(app_cache_handle_t handle,
uintptr_t offset, const void *data,
size_t data_size) {
if (!probe_read_access(data, data_size)) {
@@ -1428,7 +1428,7 @@ bool app_cache_write_image__verified(app_cache_handle_t handle,
access_violation:
apptask_access_violation();
- return false;
+ return TS_EACCES;
}
#endif // USE_APP_LOADING
diff --git a/core/embed/sys/syscall/stm32/syscall_verifiers.h b/core/embed/sys/syscall/stm32/syscall_verifiers.h
index 9e58b3f8..bb829927 100644
--- a/core/embed/sys/syscall/stm32/syscall_verifiers.h
+++ b/core/embed/sys/syscall/stm32/syscall_verifiers.h
@@ -350,15 +350,15 @@ bool tropic_data_read__verified(uint16_t udata_slot, uint8_t *data,
#include <io/app_loader.h>
-bool app_task_spawn__verified(const app_hash_t *hash, systask_id_t *task_id);
+ts_t app_task_spawn__verified(const app_hash_t *hash, systask_id_t *task_id);
-bool app_task_get_pminfo__verified(systask_id_t task_id,
+ts_t app_task_get_pminfo__verified(systask_id_t task_id,
systask_postmortem_t *pminfo);
app_cache_handle_t app_cache_create_image__verified(const app_hash_t *hash,
size_t image_size);
-bool app_cache_write_image__verified(app_cache_handle_t handle,
+ts_t app_cache_write_image__verified(app_cache_handle_t handle,
uintptr_t offset, const void *data,
size_t data_size);
diff --git a/core/embed/upymod/modtrezorapp/modtrezorapp-image.h b/core/embed/upymod/modtrezorapp/modtrezorapp-image.h
index 334f5703..ffa3819e 100644
--- a/core/embed/upymod/modtrezorapp/modtrezorapp-image.h
+++ b/core/embed/upymod/modtrezorapp/modtrezorapp-image.h
@@ -46,7 +46,8 @@ STATIC mp_obj_t mod_trezorapp_AppImage_write(mp_obj_t self, mp_obj_t offset_obj,
uintptr_t offset = mp_obj_get_int(offset_obj);
- if (!app_cache_write_image(image, offset, bufinfo.buf, bufinfo.len)) {
+ ts_t status = app_cache_write_image(image, offset, bufinfo.buf, bufinfo.len);
+ if (ts_error(status)) {
mp_raise_msg(&mp_type_RuntimeError,
MP_ERROR_TEXT("Failed to write to app image."));
}
@@ -68,7 +69,15 @@ STATIC mp_obj_t mod_trezorapp_AppImage_finalize(mp_obj_t self,
bool accept = mp_obj_is_true(accept_obj);
- app_cache_finalize_image(o->image, accept);
+ ts_t status = app_cache_finalize_image(o->image, accept);
+
+ if (accept && ts_error(status)) {
+ mp_raise_msg(&mp_type_RuntimeError,
+ MP_ERROR_TEXT("Failed to finalize app image."));
+ }
+
+ UNUSED(status);
+
o->image = APP_CACHE_INVALID_HANDLE;
return mp_const_none;
diff --git a/core/embed/upymod/modtrezorapp/modtrezorapp.c b/core/embed/upymod/modtrezorapp/modtrezorapp.c
index f82a7169..52463f93 100644
--- a/core/embed/upymod/modtrezorapp/modtrezorapp.c
+++ b/core/embed/upymod/modtrezorapp/modtrezorapp.c
@@ -52,7 +52,8 @@ STATIC mp_obj_t mod_trezorapp_spawn_task(mp_obj_t app_hash_obj) {
const app_hash_t *hash_ptr = (const app_hash_t *)hash.buf;
systask_id_t task_id;
- if (!app_task_spawn(hash_ptr, &task_id)) {
+ ts_t status = app_task_spawn(hash_ptr, &task_id);
+ if (ts_error(status)) {
mp_raise_msg(&mp_type_RuntimeError,
MP_ERROR_TEXT("Failed to spawn app from app cache"));
}
@@ -114,7 +115,8 @@ STATIC mp_obj_t mod_trezorapp_load_file(mp_obj_t app_hash_obj,
const char *filename = mp_obj_str_get_str(filename_obj);
- if (!app_cache_load_file(hash_ptr, filename)) {
+ ts_t status = app_cache_load_file(hash_ptr, filename);
+ if (ts_error(status)) {
mp_raise_msg(&mp_type_RuntimeError,
MP_ERROR_TEXT("Failed to load app image from file"));
}
Why this scored 34/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.