fix(core/bootloader): timeout FW installation when the next message doesn't arrive in time
What changed, and why it matters
This commit adds a 10-second timeout to the Trezor bootloader's firmware update process. Previously, if the host computer stopped sending firmware pieces during an update, the bootloader could wait forever in a special update mode. The fix makes the device give up and show a failure screen if no next message arrives within 10 seconds. This is a hardening change: it reduces the window during which a device is stuck in a firmware-update state, which could matter if an attacker has physical or local access, but the commit itself does not describe a specific attack or vulnerability.
Treat as a defensive hardening fix. Review whether 10 seconds is appropriate for slow or interrupted hosts, and verify that other bootloader communication loops have equivalent timeouts. No immediate incident response is indicated by the commit alone.
Security signals we found
Adds a timeout to a previously blocking/unbounded receive loop in bootloader firmware update workflow
Failure path returns WF_ERROR and shows failure screen, reducing time spent in update mode
No changelog entry and no explicit security description in commit message
Evidence from the diff
In core/embed/projects/bootloader/workflow/wf_firmware_update.c, a MESSAGE_RX_TIMEOUT of 10 000 ms is introduced. workflow_firmware_update() now initializes msg_deadline = ticks_timeout(MESSAGE_RX_TIMEOUT) before the receive loop. On each loop iteration where the expected read-ready event is not signalled, it checks ticks_expired(msg_deadline); if the deadline has passed it calls ui_screen_fail() and returns WF_ERROR. After a successful process_msg_FirmwareUpload() the deadline is reset. This prevents an indefinite wait for the next FirmwareUpload chunk. The patch is small and partial: it does not add timeout handling on message parsing or on the upload-processing path, only on the event-poll retry path.
Changed components
Trezor Core bootloader firmware update workflowcore/embed/projects/bootloader/workflow/wf_firmware_update.cInspect captured patch +11 / −0
diff --git a/core/embed/projects/bootloader/workflow/wf_firmware_update.c b/core/embed/projects/bootloader/workflow/wf_firmware_update.c
index 36aafea6..39200d7f 100644
--- a/core/embed/projects/bootloader/workflow/wf_firmware_update.c
+++ b/core/embed/projects/bootloader/workflow/wf_firmware_update.c
@@ -43,6 +43,8 @@
#include "emulator.h"
#endif
+#define MESSAGE_RX_TIMEOUT 10000
+
typedef enum {
UPLOAD_OK = 0,
UPLOAD_IN_PROGRESS = 1,
@@ -635,6 +637,8 @@ workflow_result_t workflow_firmware_update(protob_io_t *iface) {
upload_status_t s = UPLOAD_IN_PROGRESS;
+ uint32_t msg_deadline = ticks_timeout(MESSAGE_RX_TIMEOUT);
+
while (true) {
sysevents_t awaited = {0};
sysevents_t signalled = {0};
@@ -644,6 +648,11 @@ workflow_result_t workflow_firmware_update(protob_io_t *iface) {
sysevents_poll(&awaited, &signalled, ticks_timeout(100));
if (awaited.read_ready != signalled.read_ready) {
+ if (ticks_expired(msg_deadline)) {
+ // timeout
+ ui_screen_fail();
+ return WF_ERROR;
+ }
continue;
}
@@ -655,6 +664,8 @@ workflow_result_t workflow_firmware_update(protob_io_t *iface) {
}
s = process_msg_FirmwareUpload(iface, &ctx);
+ msg_deadline = ticks_timeout(MESSAGE_RX_TIMEOUT);
+
if (s < 0 && s != UPLOAD_ERR_USER_ABORT) { // error, but not user abort
if (s == UPLOAD_ERR_BOOTLOADER_LOCKED) {
// This function does not return
Why this scored 49/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.