fix(core): remove rsod screens from rtl error handling
What changed, and why it matters
This commit is a code cleanup in the Trezor firmware. It moves special error-screen functions (for wipe code, too many PIN attempts, install restricted, and device wiped) out of the runtime library (RTL) error-handling module and into a new dedicated 'rsod_special' module. It also removes the 'ensure' macro from a public header. The change is architectural refactoring; it does not appear to fix a security bug or introduce a new vulnerability. The commit message and diff give no indication this is a security patch.
No immediate security action required. Treat as normal refactoring. Reviewers may want to verify that all former callers of the moved functions still include the correct header and that the ensure() macro removal does not break any downstream code that relied on it from error_handling.h.
Security signals we found
No security-relevant functional change in moved code
Removal of ensure() macro from public header may reduce accidental misuse but is not a vulnerability fix
Refactoring touches wipe/PIN-restriction/firmware-installation screens, which are security-sensitive flows, but the logic is preserved verbatim
Evidence from the diff
The patch refactors RSOD (Red Screen of Death) handling. Functions show_wipe_code_screen(), show_pin_too_many_screen(), show_install_restricted_screen(), and show_wipe_info() are relocated from core/embed/rtl/error_handling.c to a new file core/embed/util/rsod/rsod_special.c, with a new header core/embed/util/rsod/inc/util/rsod_special.h. Build scripts (SConscript.*) add rsod_special.c to each target. The public header core/embed/rtl/inc/rtl/error_handling.h loses the ensure() macro and the special-screen declarations; common.h now includes both rtl/error_handling.h and util/rsod_special.h. Several consumers (bootloader, secret.c variants, bootutils.c, tamper.c) gain explicit includes for sys/bootutils.h or util/rsod_special.h. No functional behavior changes are visible in the moved code; it is a straight move with one include adjustment (strutils.h in the new .c file).
Changed components
core/embed/rtl/error_handling.ccore/embed/rtl/inc/rtl/error_handling.hcore/embed/rtl/inc/common.hcore/embed/util/rsod/rsod_special.ccore/embed/util/rsod/inc/util/rsod_special.hcore/embed/projects/bootloader/main.ccore/embed/projects/bootloader/fw_check.ccore/embed/projects/bootloader/workflow/wf_firmware_update.ccore/embed/sec/secret/stm32f4/secret.ccore/embed/sec/secret/stm32u5/secret.ccore/embed/sec/secret/unix/secret.ccore/embed/sys/startup/unix/bootutils.ccore/embed/sys/tamper/stm32u5/tamper.cSConscript build files for boardloader, bootloader, bootloader_ci, bootloader_emu, kernel, prodtest, prodtest_emu, secmon, unixInspect captured patch +157 / −86
diff --git a/core/SConscript.boardloader b/core/SConscript.boardloader
index 0853e408..e7664529 100644
--- a/core/SConscript.boardloader
+++ b/core/SConscript.boardloader
@@ -95,6 +95,7 @@ SOURCE_MOD += [
'embed/util/flash/flash_utils.c',
'embed/util/image/image.c',
'embed/util/rsod/rsod.c',
+ 'embed/util/rsod/rsod_special.c',
'embed/util/scm_revision/scm_revision.c',
'embed/rtl/error_handling.c',
'embed/rtl/printf.c',
diff --git a/core/SConscript.bootloader b/core/SConscript.bootloader
index 5601a9c1..6b5f2706 100644
--- a/core/SConscript.bootloader
+++ b/core/SConscript.bootloader
@@ -121,6 +121,7 @@ SOURCE_MOD += [
'embed/util/flash/flash_utils.c',
'embed/util/image/image.c',
'embed/util/rsod/rsod.c',
+ 'embed/util/rsod/rsod_special.c',
'embed/util/scm_revision/scm_revision.c',
'embed/rtl/error_handling.c',
'embed/rtl/printf.c',
diff --git a/core/SConscript.bootloader_ci b/core/SConscript.bootloader_ci
index 264fd16f..ecc7dba7 100644
--- a/core/SConscript.bootloader_ci
+++ b/core/SConscript.bootloader_ci
@@ -99,6 +99,7 @@ SOURCE_MOD += [
'embed/util/flash/flash_utils.c',
'embed/util/image/image.c',
'embed/util/rsod/rsod.c',
+ 'embed/util/rsod/rsod_special.c',
'embed/util/scm_revision/scm_revision.c',
'embed/rtl/error_handling.c',
'embed/rtl/printf.c',
diff --git a/core/SConscript.bootloader_emu b/core/SConscript.bootloader_emu
index 3097429a..996e9deb 100644
--- a/core/SConscript.bootloader_emu
+++ b/core/SConscript.bootloader_emu
@@ -94,6 +94,7 @@ SOURCE_MOD += [
'embed/util/flash/flash_utils.c',
'embed/util/image/image.c',
'embed/util/rsod/rsod.c',
+ 'embed/util/rsod/rsod_special.c',
'embed/util/scm_revision/scm_revision.c',
'embed/rtl/error_handling.c',
'embed/rtl/printf.c',
diff --git a/core/SConscript.kernel b/core/SConscript.kernel
index 19fe9dc3..82e0e885 100644
--- a/core/SConscript.kernel
+++ b/core/SConscript.kernel
@@ -220,6 +220,7 @@ SOURCE_MOD += [
'embed/util/image/boot_image.c',
'embed/util/image/image.c',
'embed/util/rsod/rsod.c',
+ 'embed/util/rsod/rsod_special.c',
'embed/rtl/error_handling.c',
'embed/rtl/printf.c',
'embed/rtl/strutils.c',
diff --git a/core/SConscript.prodtest b/core/SConscript.prodtest
index 0cb51d3b..50359b49 100644
--- a/core/SConscript.prodtest
+++ b/core/SConscript.prodtest
@@ -173,6 +173,7 @@ SOURCE_MOD += [
'embed/util/image/boot_image.c',
'embed/util/image/image.c',
'embed/util/rsod/rsod.c',
+ 'embed/util/rsod/rsod_special.c',
'embed/util/scm_revision/scm_revision.c',
'embed/rtl/cli.c',
'embed/rtl/error_handling.c',
diff --git a/core/SConscript.prodtest_emu b/core/SConscript.prodtest_emu
index 98c7981f..bb90a4d3 100644
--- a/core/SConscript.prodtest_emu
+++ b/core/SConscript.prodtest_emu
@@ -140,6 +140,7 @@ SOURCE_MOD += [
'embed/util/image/boot_image.c',
'embed/util/image/image.c',
'embed/util/rsod/rsod.c',
+ 'embed/util/rsod/rsod_special.c',
'embed/util/scm_revision/scm_revision.c',
'embed/rtl/cli.c',
'embed/rtl/error_handling.c',
diff --git a/core/SConscript.secmon b/core/SConscript.secmon
index 5baa1ef9..88b87fc0 100644
--- a/core/SConscript.secmon
+++ b/core/SConscript.secmon
@@ -215,6 +215,7 @@ SOURCE_MOD += [
'embed/util/translations/translations.c',
'embed/util/image/boot_image.c',
'embed/util/image/image.c',
+ 'embed/util/rsod/rsod_special.c',
'embed/rtl/error_handling.c',
'embed/rtl/printf.c',
'embed/rtl/strutils.c',
diff --git a/core/SConscript.unix b/core/SConscript.unix
index 90f130c4..69616adc 100644
--- a/core/SConscript.unix
+++ b/core/SConscript.unix
@@ -247,6 +247,7 @@ SOURCE_MOD += [
'embed/util/image/image.c',
'embed/util/translations/translations.c',
'embed/util/rsod/rsod.c',
+ 'embed/util/rsod/rsod_special.c',
'embed/util/scm_revision/scm_revision.c',
'embed/rtl/error_handling.c',
'embed/rtl/printf.c',
diff --git a/core/embed/projects/bootloader/fw_check.c b/core/embed/projects/bootloader/fw_check.c
index 998698ae..589f8ae2 100644
--- a/core/embed/projects/bootloader/fw_check.c
+++ b/core/embed/projects/bootloader/fw_check.c
@@ -20,6 +20,7 @@
#include <trezor_model.h>
#include <trezor_rtl.h>
+#include <sys/bootutils.h>
#include <util/flash.h>
#include <util/flash_otp.h>
#include <util/image.h>
diff --git a/core/embed/projects/bootloader/main.c b/core/embed/projects/bootloader/main.c
index f7ae76cd..10c3025d 100644
--- a/core/embed/projects/bootloader/main.c
+++ b/core/embed/projects/bootloader/main.c
@@ -34,6 +34,7 @@
#include <util/flash_utils.h>
#include <util/image.h>
#include <util/rsod.h>
+#include <util/rsod_special.h>
#include <util/unit_properties.h>
#ifdef USE_BOOT_UCB
diff --git a/core/embed/projects/bootloader/workflow/wf_firmware_update.c b/core/embed/projects/bootloader/workflow/wf_firmware_update.c
index 39200d7f..eb1ed359 100644
--- a/core/embed/projects/bootloader/workflow/wf_firmware_update.c
+++ b/core/embed/projects/bootloader/workflow/wf_firmware_update.c
@@ -25,6 +25,7 @@
#include <sys/systick.h>
#include <util/flash.h>
#include <util/flash_utils.h>
+#include <util/rsod_special.h>
#if defined(LOCKABLE_BOOTLOADER) || USE_STORAGE_HWKEY
#include <sec/secret.h>
diff --git a/core/embed/rtl/error_handling.c b/core/embed/rtl/error_handling.c
index 5fad0de1..33145dff 100644
--- a/core/embed/rtl/error_handling.c
+++ b/core/embed/rtl/error_handling.c
@@ -22,25 +22,12 @@
#include <sys/bootutils.h>
#include <sys/system.h>
-#ifdef FANCY_FATAL_ERROR
-#include "rust_ui_common.h"
-#endif
-
#ifndef TREZOR_EMULATOR
// Stack check guard value set in startup code.
// This is used if stack protection is enabled.
THREAD_LOCAL uint32_t __stack_chk_guard = 0;
#endif
-#define ALL_DATA_ERASED_MESSAGE "All data has been erased from the device"
-
-#ifdef TREZOR_MODEL_T3W1
-// empty message for T3W1 so that it falls to the more appropriate default
-#define RECONNECT_DEVICE_MESSAGE ""
-#else
-#define RECONNECT_DEVICE_MESSAGE "Please reconnect\nthe device"
-#endif
-
// Calls to this function are inserted by the compiler
// when stack protection is enabled.
void __attribute__((noreturn, used)) __stack_chk_fail(void) {
@@ -64,58 +51,3 @@ __fatal_error(const char *msg, const char *file, int line) {
while (1)
;
}
-
-void __attribute__((noreturn)) show_wipe_code_screen(void) {
- bootutils_wipe_info_t info = {0};
-
- const char *title = "Wipe code entered";
-
- strncpy(info.title, title, sizeof(info.title) - 1);
- strncpy(info.message, ALL_DATA_ERASED_MESSAGE, sizeof(info.message) - 1);
- strncpy(info.footer, RECONNECT_DEVICE_MESSAGE, sizeof(info.footer) - 1);
-
- reboot_and_wipe(&info);
-
- while (1)
- ;
-}
-
-#ifdef FANCY_FATAL_ERROR
-void show_wipe_info(const bootutils_wipe_info_t *info) {
- const char *title = "Device wiped";
- const char *message = ALL_DATA_ERASED_MESSAGE;
- const char *footer = "Please visit trezor.io/rsod";
-
- if (info->title[0] != '\0') {
- title = info->title;
- }
- if (info->message[0] != '\0') {
- message = info->message;
- }
- if (info->footer[0] != '\0') {
- footer = info->footer;
- }
-
- display_rsod_rust(title, message, footer);
-}
-#endif
-
-void __attribute__((noreturn)) show_pin_too_many_screen(void) {
- bootutils_wipe_info_t info = {0};
-
- const char *title = "Pin attempts exceeded";
-
- strncpy(info.title, title, sizeof(info.title) - 1);
- strncpy(info.message, ALL_DATA_ERASED_MESSAGE, sizeof(info.message) - 1);
- strncpy(info.footer, RECONNECT_DEVICE_MESSAGE, sizeof(info.footer) - 1);
-
- reboot_and_wipe(&info);
- while (1)
- ;
-}
-
-void __attribute__((noreturn)) show_install_restricted_screen(void) {
- error_shutdown_ex("Install restricted",
- "Installation of custom firmware is currently restricted.",
- "Please visit trezor.io/bootloader");
-}
diff --git a/core/embed/rtl/inc/common.h b/core/embed/rtl/inc/common.h
index d027122f..e76f0541 100644
--- a/core/embed/rtl/inc/common.h
+++ b/core/embed/rtl/inc/common.h
@@ -23,5 +23,6 @@
// Do not include this header or add dependencies to it unless required by
// storage.
+#include <rtl/error_handling.h>
#include <sys/systick.h>
-#include "rtl/error_handling.h"
+#include <util/rsod_special.h>
diff --git a/core/embed/rtl/inc/rtl/error_handling.h b/core/embed/rtl/inc/rtl/error_handling.h
index 43c274e0..ce451dd0 100644
--- a/core/embed/rtl/inc/rtl/error_handling.h
+++ b/core/embed/rtl/inc/rtl/error_handling.h
@@ -37,20 +37,3 @@ void __attribute__((noreturn)) error_shutdown(const char *message);
// Do not use this function directly, use the `ensure()` macro instead.
void __attribute__((noreturn))
__fatal_error(const char *msg, const char *file, int line);
-
-// Checks for an expression and if it is false, shows an error message
-// and shuts down the device.
-#define ensure(expr, msg) \
- (((expr) == sectrue) ? (void)0 : __fatal_error(msg, __FILE_NAME__, __LINE__))
-
-// Shows WIPE CODE ENTERED screeen and shuts down the device.
-void __attribute__((noreturn)) show_wipe_code_screen(void);
-
-// Shows TOO MANY PIN ATTEMPTS screen and shuts down the device.
-void __attribute__((noreturn)) show_pin_too_many_screen(void);
-
-// Shows INSTALL RESTRICTED screen and shuts down the device.
-void __attribute__((noreturn)) show_install_restricted_screen(void);
-
-// Shows wipe information screen
-void show_wipe_info(const bootutils_wipe_info_t *info);
diff --git a/core/embed/sec/secret/stm32f4/secret.c b/core/embed/sec/secret/stm32f4/secret.c
index 891a4ed3..b20d25fb 100644
--- a/core/embed/sec/secret/stm32f4/secret.c
+++ b/core/embed/sec/secret/stm32f4/secret.c
@@ -24,6 +24,7 @@
#include <sys/mpu.h>
#include <util/flash.h>
#include <util/flash_utils.h>
+#include <util/rsod_special.h>
#ifdef KERNEL_MODE
diff --git a/core/embed/sec/secret/stm32u5/secret.c b/core/embed/sec/secret/stm32u5/secret.c
index 44e711e7..0bd94cb8 100644
--- a/core/embed/sec/secret/stm32u5/secret.c
+++ b/core/embed/sec/secret/stm32u5/secret.c
@@ -28,6 +28,7 @@
#include <sys/mpu.h>
#include <util/flash.h>
#include <util/flash_utils.h>
+#include <util/rsod_special.h>
#include "memzero.h"
#ifdef SECURE_MODE
diff --git a/core/embed/sec/secret/unix/secret.c b/core/embed/sec/secret/unix/secret.c
index c902d696..4e6139d2 100644
--- a/core/embed/sec/secret/unix/secret.c
+++ b/core/embed/sec/secret/unix/secret.c
@@ -23,6 +23,7 @@
#include <trezor_rtl.h>
#include <sec/secret.h>
+#include <util/rsod_special.h>
#ifdef KERNEL_MODE
diff --git a/core/embed/sys/startup/unix/bootutils.c b/core/embed/sys/startup/unix/bootutils.c
index feade233..12b83ba8 100644
--- a/core/embed/sys/startup/unix/bootutils.c
+++ b/core/embed/sys/startup/unix/bootutils.c
@@ -25,6 +25,7 @@
#include <sys/bootargs.h>
#include <sys/bootutils.h>
#include <sys/systick.h>
+#include <util/rsod_special.h>
LOG_DECLARE(bootutils)
diff --git a/core/embed/sys/tamper/stm32u5/tamper.c b/core/embed/sys/tamper/stm32u5/tamper.c
index 2dbc4354..35753752 100644
--- a/core/embed/sys/tamper/stm32u5/tamper.c
+++ b/core/embed/sys/tamper/stm32u5/tamper.c
@@ -20,6 +20,7 @@
#include <trezor_bsp.h>
#include <trezor_rtl.h>
+#include <sys/bootutils.h>
#include <sys/irq.h>
#include <sys/mpu.h>
#include <sys/systick.h>
diff --git a/core/embed/util/rsod/inc/util/rsod_special.h b/core/embed/util/rsod/inc/util/rsod_special.h
new file mode 100644
index 00000000..0a1b0044
--- /dev/null
+++ b/core/embed/util/rsod/inc/util/rsod_special.h
@@ -0,0 +1,46 @@
+/*
+ * This file is part of the Trezor project, https://trezor.io/
+ *
+ * Copyright (c) SatoshiLabs
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <sys/bootutils.h>
+
+/**
+ * Shows RSOD screen with "Wipe code entered" message
+ * and shuts down the device.
+ */
+void __attribute__((noreturn)) show_wipe_code_screen(void);
+
+/**
+ * Shows RSOD screen with "Pin attempts exceeded" message
+ * and shuts down the device.
+ */
+void __attribute__((noreturn)) show_pin_too_many_screen(void);
+
+/**
+ * Shows RSOD screen with "Install restricted" message
+ * and shuts down the device.
+ */
+void __attribute__((noreturn)) show_install_restricted_screen(void);
+
+/**
+ * Shows RSOD screen with "Device wiped" message
+ * and shuts down the device.
+ */
+void show_wipe_info(const bootutils_wipe_info_t *info);
diff --git a/core/embed/util/rsod/rsod_special.c b/core/embed/util/rsod/rsod_special.c
new file mode 100644
index 00000000..6175531c
--- /dev/null
+++ b/core/embed/util/rsod/rsod_special.c
@@ -0,0 +1,92 @@
+/*
+ * This file is part of the Trezor project, https://trezor.io/
+ *
+ * Copyright (c) SatoshiLabs
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <trezor_rtl.h>
+
+#include <rtl/strutils.h>
+#include <sys/bootutils.h>
+#include <sys/system.h>
+
+#ifdef FANCY_FATAL_ERROR
+#include "rust_ui_common.h"
+#endif
+
+#define ALL_DATA_ERASED_MESSAGE "All data has been erased from the device"
+
+#ifdef TREZOR_MODEL_T3W1
+// empty message for T3W1 so that it falls to the more appropriate default
+#define RECONNECT_DEVICE_MESSAGE ""
+#else
+#define RECONNECT_DEVICE_MESSAGE "Please reconnect\nthe device"
+#endif
+
+void __attribute__((noreturn)) show_wipe_code_screen(void) {
+ bootutils_wipe_info_t info = {0};
+
+ const char *title = "Wipe code entered";
+
+ strncpy(info.title, title, sizeof(info.title) - 1);
+ strncpy(info.message, ALL_DATA_ERASED_MESSAGE, sizeof(info.message) - 1);
+ strncpy(info.footer, RECONNECT_DEVICE_MESSAGE, sizeof(info.footer) - 1);
+
+ reboot_and_wipe(&info);
+
+ while (1)
+ ;
+}
+
+#ifdef FANCY_FATAL_ERROR
+void show_wipe_info(const bootutils_wipe_info_t *info) {
+ const char *title = "Device wiped";
+ const char *message = ALL_DATA_ERASED_MESSAGE;
+ const char *footer = "Please visit trezor.io/rsod";
+
+ if (info->title[0] != '\0') {
+ title = info->title;
+ }
+ if (info->message[0] != '\0') {
+ message = info->message;
+ }
+ if (info->footer[0] != '\0') {
+ footer = info->footer;
+ }
+
+ display_rsod_rust(title, message, footer);
+}
+#endif
+
+void __attribute__((noreturn)) show_pin_too_many_screen(void) {
+ bootutils_wipe_info_t info = {0};
+
+ const char *title = "Pin attempts exceeded";
+
+ strncpy(info.title, title, sizeof(info.title) - 1);
+ strncpy(info.message, ALL_DATA_ERASED_MESSAGE, sizeof(info.message) - 1);
+ strncpy(info.footer, RECONNECT_DEVICE_MESSAGE, sizeof(info.footer) - 1);
+
+ reboot_and_wipe(&info);
+ while (1)
+ ;
+}
+
+void __attribute__((noreturn)) show_install_restricted_screen(void) {
+ error_shutdown_ex("Install restricted",
+ "Installation of custom firmware is currently restricted.",
+ "Please visit trezor.io/bootloader");
+}
Why this scored 20/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.