fix(core): fix usb driver event signalling
What changed, and why it matters
This commit fixes a minor USB status display bug in Trezor hardware wallets. The device could briefly show a false 'NO USB CONNECTION' warning on its home screen due to timing glitches when reading the USB connection state. The fix adds proper interrupt protection around shared timing data and refreshes the connection timestamp more frequently. There is no direct evidence this is a security vulnerability.
Treat as a normal bug fix. No urgent security action required. If auditing, verify that all accesses to drv->ready_time are now IRQ-protected and that the 2000 ms grace period cannot mask a genuine disconnection in security-sensitive flows.
Security signals we found
Race condition in shared driver state variable (ready_time) mitigated by IRQ lock/unlock
Timer comparison changed from unsigned to signed to handle overflow more safely
USB connection state used for UI warning and potentially power/connection decisions
Evidence from the diff
The change modifies the STM32 USB driver in Trezor’s embedded firmware. It wraps reads/writes of drv->ready_time with irq_lock()/irq_unlock() to prevent race conditions between the USB state check and the SOF (Start-of-Frame) interrupt handler. It also moves the 2000 ms ‘recently ready’ grace-period logic into a separate branch and updates ready_time in the SOF callback. The changelog frames this as fixing a false ‘NO USB CONNECTION’ warning.
Changed components
core/embed/io/usb/stm32/usb.cUSB driver state machineHome screen USB connection indicatorInspect captured patch +20 / −10
diff --git a/core/.changelog.d/5980.fixed b/core/.changelog.d/5980.fixed
new file mode 100644
index 000000000..35aaa4f2d
--- /dev/null
+++ b/core/.changelog.d/5980.fixed
@@ -0,0 +1 @@
+Fix false "NO USB CONNECTION" warning on the home screen.
diff --git a/core/embed/io/usb/stm32/usb.c b/core/embed/io/usb/stm32/usb.c
index 1586e5ba0..8dae41080 100644
--- a/core/embed/io/usb/stm32/usb.c
+++ b/core/embed/io/usb/stm32/usb.c
@@ -24,6 +24,7 @@
#include <io/usb.h>
#include <sec/random_delays.h>
+#include <sys/irq.h>
#include <sys/sysevent_source.h>
#include <sys/systick.h>
@@ -295,18 +296,24 @@ static secbool usb_configured(void) {
ready = sectrue;
}
- // This is a workaround to handle the glitches in the USB connection,
- // especially for USB-powered-only devices. This should be
- // revisited and probably fixed elsewhere.
-
- uint32_t ticks = hal_ticks_ms();
+ uint32_t now = hal_ticks_ms();
if (ready == sectrue) {
- drv->ready_time = ticks;
- } else if ((drv->was_ready == sectrue) && (ticks - drv->ready_time) < 2000) {
- // NOTE: When the timer overflows the timeout is shortened.
- // We are ignoring it for now.
- ready = sectrue;
+ irq_key_t irq_key = irq_lock();
+ drv->ready_time = now;
+ irq_unlock(irq_key);
+ } else {
+ // This is a workaround to handle the glitches in the USB connection,
+ // especially for USB-powered-only devices. This should be
+ // revisited and probably fixed elsewhere.
+
+ irq_key_t irq_key = irq_lock();
+ bool ready_recently = (int32_t)(now - drv->ready_time) < 2000;
+ irq_unlock(irq_key);
+
+ if ((drv->was_ready == sectrue) && ready_recently) {
+ ready = sectrue;
+ }
}
return ready;
@@ -728,6 +735,8 @@ static uint8_t usb_class_data_out(USBD_HandleTypeDef *dev, uint8_t ep_num) {
static uint8_t usb_class_sof(USBD_HandleTypeDef *dev) {
usb_driver_t *drv = &g_usb_driver;
+ drv->ready_time = hal_ticks_ms();
+
for (int i = 0; i < USBD_MAX_NUM_INTERFACES; i++) {
usb_iface_t *iface = &drv->ifaces[i];
if (iface->class != NULL && iface->class->SOF != NULL) {
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.