feat(core): add support for rwpi in applets
What changed, and why it matters
This commit adds plumbing for a new hardware feature called RWPI (Read-Write Position Independent code) in small helper programs ('applets') running on Trezor devices. It stores a 'static base' address in each task and sets register r9 when starting an applet. The change itself is a feature addition, not a visible bug fix, and there is no direct evidence in the commit that it fixes a security vulnerability. It may be defensive groundwork for safer dynamic app loading, but that is speculative.
Treat as a normal feature commit. If RWPI is being introduced to support dynamically loaded third-party apps, review the full design for correct sb_addr assignment, isolation between applets, and validation of applet headers. No immediate security patch action is indicated by this commit alone.
Security signals we found
Feature addition for position-independent code support, which can reduce attack surface by enabling ASLR-like relocation of writable segments
Register r9 initialization from a new task metadata field; misuse or incorrect sb_addr could affect applet memory addressing
No explicit security claim, CVE reference, or bug description in commit or supplied references
Evidence from the diff
The patch extends the systask/applet subsystem in the Trezor Core firmware to support RWPI (Read-Write Position Independent) applets. It adds an sb_addr field to systask_t, passes it through systask_init(), and initializes r9 (the ARM static base register) with that address when pushing a task’s initial register frame. For existing applets the sb_addr is hard-coded to 0, so behavior is unchanged for current use cases. The commit message frames this as a feature (‘feat(core)’) and explicitly notes ‘[no changelog]’.
Changed components
core/embed/sys/task/inc/sys/systask.hcore/embed/sys/task/stm32/applet.ccore/embed/sys/task/stm32/systask.cInspect captured patch +12 / −4
diff --git a/core/embed/sys/task/inc/sys/systask.h b/core/embed/sys/task/inc/sys/systask.h
index 7c9a797dc..54f66acd5 100644
--- a/core/embed/sys/task/inc/sys/systask.h
+++ b/core/embed/sys/task/inc/sys/systask.h
@@ -140,6 +140,10 @@ typedef struct {
// Original stack end
uint32_t stack_end;
+ // Static base (SB) address of RW segment
+ // used with dynamically linked apps, otherwise set to 0.
+ uint32_t sb_addr;
+
// Set if the task is processing the kernel callback
bool in_callback;
@@ -163,7 +167,7 @@ void systask_yield_to(systask_t* task);
//
// The task must be not be running when the function is called
bool systask_init(systask_t* task, uint32_t stack_base, uint32_t stack_size,
- void* context);
+ uint32_t sb_addr, void* context);
// Returns true if the task is alive (not terminated, killed or crashed)
bool systask_is_alive(const systask_t* task);
diff --git a/core/embed/sys/task/stm32/applet.c b/core/embed/sys/task/stm32/applet.c
index 273a4fd55..c8ee25b5f 100644
--- a/core/embed/sys/task/stm32/applet.c
+++ b/core/embed/sys/task/stm32/applet.c
@@ -59,7 +59,7 @@ bool applet_reset(applet_t* applet, uint32_t cmd, const void* arg,
const applet_header_t* header = (applet_header_t*)applet->layout.code1.start;
// Reset the applet task (stack pointer, etc.)
- if (!systask_init(&applet->task, header->stack.start, header->stack.size,
+ if (!systask_init(&applet->task, header->stack.start, header->stack.size, 0,
applet)) {
return false;
}
diff --git a/core/embed/sys/task/stm32/systask.c b/core/embed/sys/task/stm32/systask.c
index 2107c3da3..5fc87cff6 100644
--- a/core/embed/sys/task/stm32/systask.c
+++ b/core/embed/sys/task/stm32/systask.c
@@ -151,7 +151,7 @@ static systask_id_t systask_get_unused_id(void) {
}
bool systask_init(systask_t* task, uint32_t stack_base, uint32_t stack_size,
- void* applet) {
+ uint32_t sb_addr, void* applet) {
systask_id_t id = systask_get_unused_id();
if (id >= SYSTASK_MAX_TASKS) {
return false;
@@ -170,6 +170,7 @@ bool systask_init(systask_t* task, uint32_t stack_base, uint32_t stack_size,
task->stack_base = stack_base;
task->stack_end = stack_base + stack_size;
task->applet = applet;
+ task->sb_addr = sb_addr;
// Notify all event sources about the task creation
sysevents_notify_task_created(task);
@@ -224,9 +225,12 @@ bool systask_push_call(systask_t* task, void* entrypoint, uint32_t arg1,
}
// Registers r4-r11
- if (systask_push_data(task, NULL, 0x20) == NULL) {
+ uint32_t regs[8] = {0};
+ regs[9 - 4] = task->sb_addr; // r9 = Static base address
+ if (systask_push_data(task, regs, 0x20) == NULL) {
goto cleanup;
}
+
// Registers s16-s31
if (systask_push_data(task, NULL, 0x40) == NULL) {
goto cleanup;
Why this scored 25/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.