fix(core): fix returning from spawned applet to coreapp
What changed, and why it matters
This commit fixes a scheduling bug in the Trezor firmware's task-switching system. When the device switched from a small temporary 'applet' back to the main 'coreapp', the old code simply jumped tasks without telling the event scheduler to put the current task back on the runnable list. That could leave the current task unscheduled or cause a null-pointer write when the scheduler later tried to record which events woke it up. The patch adds a proper 'yield and reschedule' helper and guards the null-pointer write.
Treat as a reliability and potential security fix. Review whether the old direct yield could cause denial-of-service (unscheduled coreapp) or memory corruption (null-pointer write) on real hardware, and include in release notes if a security boundary is crossed by spawned applets.
Security signals we found
Null-pointer dereference guard added in scheduler hot path
Task scheduling state inconsistency fixed in applet-to-coreapp transition
Kernel-mode task yield behavior changed to use dispatcher-managed rescheduling
Evidence from the diff
applet_run() previously called systask_yield_to(&applet->task) directly. That bypassed the sysevent dispatcher, so the outgoing task was not re-inserted into the poller list. The new sysevents_yield_and_reschedule() inserts the active task as a poller with awaited=0, signalled=0, signalled_arg=NULL, and deadline=0, then yields. A related fix in sysevents_poll() now checks signalled_arg != NULL before dereferencing it, preventing a null-pointer store when a poller has no result argument.
Changed components
core/embed/sys/task/applet.ccore/embed/sys/task/sysevent.ccore/embed/sys/task/inc/sys/sysevent_source.hInspect captured patch +43 / −2
diff --git a/core/embed/sys/task/applet.c b/core/embed/sys/task/applet.c
index a19467cf1..167029cba 100644
--- a/core/embed/sys/task/applet.c
+++ b/core/embed/sys/task/applet.c
@@ -20,6 +20,7 @@
#include <trezor_rtl.h>
#include <sys/applet.h>
+#include <sys/sysevent_source.h>
#include <sys/systask.h>
#ifdef KERNEL
@@ -35,7 +36,9 @@ void applet_init(applet_t* applet, const applet_privileges_t* privileges,
}
}
-void applet_run(applet_t* applet) { systask_yield_to(&applet->task); }
+void applet_run(applet_t* applet) {
+ sysevents_yield_and_reschedule(&applet->task);
+}
void applet_unload(applet_t* applet) {
if (systask_is_alive(&applet->task)) {
diff --git a/core/embed/sys/task/inc/sys/sysevent_source.h b/core/embed/sys/task/inc/sys/sysevent_source.h
index 28b64e121..98f3cd354 100644
--- a/core/embed/sys/task/inc/sys/sysevent_source.h
+++ b/core/embed/sys/task/inc/sys/sysevent_source.h
@@ -118,4 +118,13 @@ void sysevents_notify_task_created(systask_t *task);
// event source.
void sysevents_notify_task_killed(systask_t *task);
+// Yields the CPU from the active task and schedules it to be resumed as
+// soon as possible.
+//
+// Adds the current active task to the polling list without waiting for any
+// specific events and with an immediate timeout of 0. This effectively
+// schedules the task to resume as soon as possible when another task
+// gives up the CPU.
+void sysevents_yield_and_reschedule(systask_t *task);
+
#endif // KERNEL_MODE
diff --git a/core/embed/sys/task/sysevent.c b/core/embed/sys/task/sysevent.c
index c3f9ae934..70d8d2181 100644
--- a/core/embed/sys/task/sysevent.c
+++ b/core/embed/sys/task/sysevent.c
@@ -275,7 +275,9 @@ void sysevents_poll(const sysevents_t *awaited, sysevents_t *signalled,
mpu_set_active_applet(&applet->layout);
}
#endif
- *poller->signalled_arg = poller->signalled;
+ if (poller->signalled_arg != NULL) {
+ *poller->signalled_arg = poller->signalled;
+ }
remove_poller(dispatcher, prio);
if (task == kernel_task) {
return;
@@ -332,6 +334,33 @@ void sysevents_notify_task_killed(systask_t *task) {
}
}
+// Yields the active task and schedules it to be resumed
+//
+// Adds the current active taqsk to the polling list without waiting for any
+// specific events and with an immediate timeout of 0. This effectively
+// schedules the task to resume as soon as possible when the
+void sysevents_yield_and_reschedule(systask_t *task) {
+ sysevent_dispatcher_t *dispatcher = &g_sysevent_dispatcher;
+
+ systask_t *active_task = systask_active();
+
+ if (active_task != systask_kernel()) {
+ uint32_t prio = dispatcher->pollers_count;
+
+ insert_poller(dispatcher, prio);
+
+ // Add task to the polling list
+ // (Do not wait for any events, just scschedule it)
+ dispatcher->pollers[prio].task = active_task;
+ dispatcher->pollers[prio].awaited = (sysevents_t){0};
+ dispatcher->pollers[prio].signalled = (sysevents_t){0};
+ dispatcher->pollers[prio].signalled_arg = NULL;
+ dispatcher->pollers[prio].deadline = ticks_timeout(0);
+ }
+
+ systask_yield_to(task);
+}
+
#endif // KERNEL_MODE
ssize_t syshandle_read_blocking(syshandle_t handle, void *buffer,
Why this scored 45/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.