fix(core): call SDL drawing function in kernel task context
What changed, and why it matters
This commit changes how the Trezor emulator (Unix/SDL build) redraws the screen and the suspend overlay. Previously, SDL drawing functions could be called from any internal task. The patch forces those SDL calls to run in the special 'kernel task' context, likely to avoid crashes, graphics corruption, or thread-safety problems when another task tries to update the display. It is a hardening/robustness fix for the emulator, not a live hardware wallet fix, and there is no direct evidence it closes a user-exploitable security vulnerability.
Treat as a low-severity hardening fix for the emulator. If running the Unix emulator, update to include this commit to avoid display-related instability. No urgent action is warranted for hardware devices or production firmware unless further analysis shows the same task-context issue exists on real hardware. Request a short note from the vendor clarifying whether this was a crash fix or a security boundary fix.
Security signals we found
SDL drawing marshalled to kernel task context
Use of systask_push_call / systask_yield_to trampoline pattern
Comment explicitly states purpose: 'Call SDL drawing function in the context of the kernel task'
No changelog entry ([no changelog])
No CVE, advisory, or researcher attribution in commit
Evidence from the diff
The diff rewrites display_refresh() and display_draw_suspend_overlay() in core/embed/io/display/unix/display_driver.c so that the actual SDL rendering work is dispatched to systask_kernel() via systask_push_call() and a trampoline when the caller is not already running in the kernel task. The internal logic of the SDL draw calls is unchanged; only the task context is switched. This suggests the underlying SDL renderer/texture operations are not safe to call from arbitrary systasks, possibly because of single-threaded SDL assumptions, lack of locking, or event-loop ownership. The patch is defensive and partial: it covers two entry points but does not show a broader audit of all SDL-using code paths.
Changed components
Trezor firmware emulator / Unix buildcore/embed/io/display/unix/display_driver.cSDL display driverPower-manager suspend overlay (USE_POWER_MANAGER)Inspect captured patch +40 / −2
diff --git a/core/embed/io/display/unix/display_driver.c b/core/embed/io/display/unix/display_driver.c
index d57c060a..d91ec3ed 100644
--- a/core/embed/io/display/unix/display_driver.c
+++ b/core/embed/io/display/unix/display_driver.c
@@ -30,6 +30,7 @@
#include <io/display.h>
#include <io/unix/sdl_display.h>
#include <sys/logging.h>
+#include <sys/systask.h>
#include <SDL.h>
#include <SDL_image.h>
@@ -401,7 +402,7 @@ static SDL_Rect screen_rect(void) {
}
}
-void display_refresh(void) {
+static void display_refresh_internal(void) {
display_driver_t *drv = &g_display_driver;
if (!drv->initialized) {
@@ -434,6 +435,23 @@ void display_refresh(void) {
SDL_RenderPresent(drv->renderer);
}
+static void display_refresh_trampoline(uintptr_t arg1, uintptr_t arg2,
+ uintptr_t arg3) {
+ display_refresh_internal();
+ systask_yield_to((systask_t *)arg1);
+}
+
+void display_refresh(void) {
+ // Call SDL drawing function in the context of the kernel task
+ if (systask_active() == systask_kernel()) {
+ display_refresh_internal();
+ } else {
+ systask_push_call(systask_kernel(), (void *)display_refresh_trampoline,
+ (uintptr_t)systask_active(), 0, 0);
+ systask_yield_to(systask_kernel());
+ }
+}
+
#ifndef DISPLAY_MONO
void display_fill(const gfx_bitblt_t *bb) {
@@ -572,7 +590,7 @@ void display_clear_save(void) {
}
#ifdef USE_POWER_MANAGER
-void display_draw_suspend_overlay(void) {
+static void display_draw_suspend_overlay_internal(void) {
display_driver_t *drv = &g_display_driver;
if (!drv->initialized) {
@@ -612,4 +630,24 @@ void display_draw_suspend_overlay(void) {
SDL_DestroyTexture(overlay);
SDL_SetRenderDrawColor(drv->renderer, 0, 0, 0, 255);
}
+
+static void display_draw_suspend_overlay_trampoline(uintptr_t arg1,
+ uintptr_t arg2,
+ uintptr_t arg3) {
+ display_draw_suspend_overlay_internal();
+ systask_yield_to((systask_t *)arg1);
+}
+
+void display_draw_suspend_overlay(void) {
+ // Call SDL drawing function in the context of the kernel task
+ if (systask_active() == systask_kernel()) {
+ display_draw_suspend_overlay_internal();
+ } else {
+ systask_push_call(systask_kernel(),
+ (void *)display_draw_suspend_overlay_trampoline,
+ (uintptr_t)systask_active(), 0, 0);
+ systask_yield_to(systask_kernel());
+ }
+}
+
#endif
Why this scored 23/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.