What changed, and why it matters
This commit fixes a bug where Trezor hardware wallets would not display the intended Red Screen of Death (RSOD) warning when a physical tamper-detection event occurred. The fix reorganizes the tamper interrupt handler so it correctly rebuilds the error information and uses the proper RSOD display path after a tamper event. Without the fix, the device might fail to show the user-visible tamper alert, which could hide that the device had detected a physical attack.
Treat as a reliability/anti-tamper UI fix. Review whether the previous code path could crash or silently reboot instead of showing the RSOD, and verify that the new path displays the tamper alert correctly across boardloader and firmware builds. No immediate exploit mitigation is indicated by the diff alone.
Security signals we found
Tamper-detection alert display failure
RSOD (Red Screen of Death) not shown on physical tamper event
MPU re-initialization added after tamper RAM erase
Interrupt handler refactored to use postmortem/reboot path
Evidence from the diff
The patch refactors the STM32U5 tamper interrupt handler (TAMP_IRQHandler) in core/embed/sys/tamper/stm32u5/tamper.c. Previously the handler called error_shutdown_ex(“TAMPER”, …) directly. The change introduces tamper_build_pminfo() to populate a systask_postmortem_t structure and then calls either reboot_with_rsod(&pminfo) or rsod_panic_handler(&pminfo), depending on configuration. It also adds an mpu_init() call before mpu_reconfig() because tamper detection erases parts of RAM, requiring MPU re-initialization. The changelog states the user-visible effect: “Fixed tamper RSOD not showing.”
Changed components
core/embed/sys/tamper/stm32u5/tamper.cTAMP_IRQHandler tamper interrupt handlerSTM32U5 secure-mode tamper subsystemInspect captured patch +50 / −28
diff --git a/core/.changelog.d/6165.fixed b/core/.changelog.d/6165.fixed
new file mode 100644
index 000000000..0fffc2372
--- /dev/null
+++ b/core/.changelog.d/6165.fixed
@@ -0,0 +1 @@
+Fixed tamper RSOD not showing.
diff --git a/core/embed/sys/tamper/stm32u5/tamper.c b/core/embed/sys/tamper/stm32u5/tamper.c
index 7bb69c60d..1e4bbefbd 100644
--- a/core/embed/sys/tamper/stm32u5/tamper.c
+++ b/core/embed/sys/tamper/stm32u5/tamper.c
@@ -24,6 +24,7 @@
#include <sys/mpu.h>
#include <sys/systick.h>
#include <sys/tamper.h>
+#include <util/rsod.h>
#ifdef SECURE_MODE
@@ -198,52 +199,72 @@ void tamper_external_enable(void) {
#endif
}
-// Interrupt handle for all tamper events
-// It displays an error message
-void TAMP_IRQHandler(void) {
- mpu_reconfig(MPU_MODE_DEFAULT);
+void tamper_build_pminfo(systask_postmortem_t* pminfo, uint32_t tamper_sr) {
+ const char* title = "TAMPER";
- // Disable external tamper, as its level detected
- // and it would trigger again. We don't need it until reset.
-#ifdef TAMPER_INPUT_2
- TAMP->CR1 &= ~TAMP_CR1_TAMP2E;
-#endif
+ memset(pminfo, 0, sizeof(*pminfo));
+ pminfo->reason = TASK_TERM_REASON_ERROR;
+ memcpy(pminfo->error.title, title, strlen(title));
- uint32_t sr = TAMP->SR;
- TAMP->SCR = sr;
+#ifndef BOARDLOADER
-#ifdef BOARDLOADER
- error_shutdown_ex("TAMPER", NULL, NULL);
-#else
const char* reason = "UNKNOWN";
- if (sr & TAMP_SR_TAMP1F) {
+ if (tamper_sr & TAMP_SR_TAMP1F) {
reason = "INPUT1";
- } else if (sr & TAMP_SR_TAMP2F) {
+ } else if (tamper_sr & TAMP_SR_TAMP2F) {
reason = "INPUT2";
- } else if (sr & TAMP_SR_ITAMP1F) {
+ } else if (tamper_sr & TAMP_SR_ITAMP1F) {
reason = "VOLTAGE";
- } else if (sr & TAMP_SR_ITAMP2F) {
+ } else if (tamper_sr & TAMP_SR_ITAMP2F) {
reason = "TEMPERATURE";
- } else if (sr & TAMP_SR_ITAMP3F) {
+ } else if (tamper_sr & TAMP_SR_ITAMP3F) {
reason = "LSE CLOCK";
- } else if (sr & TAMP_SR_ITAMP5F) {
+ } else if (tamper_sr & TAMP_SR_ITAMP5F) {
reason = "RTC OVERFLOW";
- } else if (sr & TAMP_SR_ITAMP6F) {
+ } else if (tamper_sr & TAMP_SR_ITAMP6F) {
reason = "SWD ACCESS";
- } else if (sr & TAMP_SR_ITAMP7F) {
+ } else if (tamper_sr & TAMP_SR_ITAMP7F) {
reason = "ANALOG WDG1";
- } else if (sr & TAMP_SR_ITAMP8F) {
+ } else if (tamper_sr & TAMP_SR_ITAMP8F) {
reason = "MONO COUNTER";
- } else if (sr & TAMP_SR_ITAMP9F) {
+ } else if (tamper_sr & TAMP_SR_ITAMP9F) {
reason = "CRYPTO ERROR";
- } else if (sr & TAMP_SR_ITAMP11F) {
+ } else if (tamper_sr & TAMP_SR_ITAMP11F) {
reason = "IWDG";
- } else if (sr & TAMP_SR_ITAMP12F) {
+ } else if (tamper_sr & TAMP_SR_ITAMP12F) {
reason = "ANALOG WDG2";
- } else if (sr & TAMP_SR_ITAMP13F) {
+ } else if (tamper_sr & TAMP_SR_ITAMP13F) {
reason = "ANALOG WDG3";
}
- error_shutdown_ex("TAMPER", reason, NULL);
+ memcpy(pminfo->error.message, reason, strlen(reason));
+#endif
+}
+
+// Interrupt handle for all tamper events
+// It displays an error message
+void TAMP_IRQHandler(void) {
+ // tamper erases part of RAM so we need to reconfigure whatever is to be used,
+ // such as re-initialize MPU as it is used in reboot_with_rsod
+ mpu_init();
+
+ mpu_reconfig(MPU_MODE_DEFAULT);
+
+ // Disable external tamper, as its level detected
+ // and it would trigger again. We don't need it until reset.
+#ifdef TAMPER_INPUT_2
+ TAMP->CR1 &= ~TAMP_CR1_TAMP2E;
+#endif
+
+ uint32_t tamper_sr = TAMP->SR;
+ TAMP->SCR = tamper_sr;
+
+ systask_postmortem_t pminfo;
+ tamper_build_pminfo(&pminfo, tamper_sr);
+
+#if defined(USE_BOOTARGS_RSOD) && !defined(BOARDLOADER)
+ reboot_with_rsod(&pminfo);
+#else
+ rsod_panic_handler(&pminfo);
#endif
}
Why this scored 42/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.