fix(core): restart nRF chip on restart from menu
What changed, and why it matters
This update fixes a bug where rebooting a Trezor device from its on-device menu did not properly restart the Bluetooth chip (nRF). The fix makes sure the Bluetooth chip is turned off and rebooted before the device restarts, so both sides come back up in a clean, synchronized state. Without this, Bluetooth could be left in a confused or half-working state after a reboot.
Treat as a reliability/robustness fix rather than an urgent security patch. Review whether a stuck or partially initialized nRF could be induced by an attacker to influence post-reboot pairing or firmware-update behavior, and consider hardening the nRF boot handshake independently.
Security signals we found
Bluetooth/nRF controller not reset on user-initiated reboot before fix
New syscall added to allow privileged nRF reboot from unprivileged contexts
Bootloader and MicroPython reboot paths now synchronized on nRF reset
Changelog frames change as a fix, not a security fix
Evidence from the diff
The commit adds a new SYSCALL_NRF_REBOOT syscall and exposes nrf_reboot() to the bootloader and MicroPython layer. On a menu-triggered reboot, the code now calls ble_switch_off() and nrf_reboot() before reboot_device()/reboot_with_fade(). Previously only the firmware-install and wipe/unlock paths rebooted cleanly; the explicit user-reboot path did not reset the nRF/Bluetooth controller, potentially leaving it in an inconsistent state across the reboot boundary.
Changed components
core/embed/projects/bootloader/main.ccore/embed/sys/syscall/inc/sys/syscall_numbers.hcore/embed/sys/syscall/stm32/syscall_dispatch.ccore/embed/sys/syscall/stm32/syscall_stubs.ccore/embed/upymod/modtrezorutils/modtrezorutils.cTrezor Core Bluetooth/nRF subsystemInspect captured patch +29 / −1
diff --git a/core/.changelog.d/6023.fixed b/core/.changelog.d/6023.fixed
new file mode 100644
index 00000000..d99ca45f
--- /dev/null
+++ b/core/.changelog.d/6023.fixed
@@ -0,0 +1 @@
+Restart bluetooth on reboot from device menu.
diff --git a/core/embed/projects/bootloader/.changelog.d/6023.fixed b/core/embed/projects/bootloader/.changelog.d/6023.fixed
new file mode 100644
index 00000000..d99ca45f
--- /dev/null
+++ b/core/embed/projects/bootloader/.changelog.d/6023.fixed
@@ -0,0 +1 @@
+Restart bluetooth on reboot from device menu.
diff --git a/core/embed/projects/bootloader/main.c b/core/embed/projects/bootloader/main.c
index 04cd5fb4..d1bd58d3 100644
--- a/core/embed/projects/bootloader/main.c
+++ b/core/embed/projects/bootloader/main.c
@@ -80,6 +80,9 @@
#ifdef USE_IWDG
#include <sec/iwdg.h>
#endif
+#ifdef USE_NRF
+#include <io/nrf.h>
+#endif
#ifdef USE_BLE
#include "wire/wire_iface_ble.h"
@@ -733,10 +736,16 @@ int bootloader_main(void) {
}
switch (result) {
- case WF_OK_FIRMWARE_INSTALLED:
case WF_OK_REBOOT_SELECTED:
+#ifdef USE_BLE
+ ble_switch_off();
+#endif
+#ifdef USE_NRF
+ nrf_reboot();
+#endif
reboot_with_fade();
break;
+ case WF_OK_FIRMWARE_INSTALLED:
case WF_OK_DEVICE_WIPED:
case WF_OK_BOOTLOADER_UNLOCKED:
reboot_with_fade();
diff --git a/core/embed/sys/syscall/inc/sys/syscall_numbers.h b/core/embed/sys/syscall/inc/sys/syscall_numbers.h
index d19b44c6..1564fc00 100644
--- a/core/embed/sys/syscall/inc/sys/syscall_numbers.h
+++ b/core/embed/sys/syscall/inc/sys/syscall_numbers.h
@@ -144,6 +144,7 @@ typedef enum {
SYSCALL_NRF_UPDATE,
SYSCALL_NRF_GET_VERSION,
SYSCALL_NRF_AUTHENTICATE,
+ SYSCALL_NRF_REBOOT,
SYSCALL_POWER_MANAGER_SUSPEND,
SYSCALL_POWER_MANAGER_HIBERNATE,
diff --git a/core/embed/sys/syscall/stm32/syscall_dispatch.c b/core/embed/sys/syscall/stm32/syscall_dispatch.c
index 145f1d57..44ea46de 100644
--- a/core/embed/sys/syscall/stm32/syscall_dispatch.c
+++ b/core/embed/sys/syscall/stm32/syscall_dispatch.c
@@ -716,6 +716,10 @@ __attribute((no_stack_protector)) void syscall_handler(uint32_t *args,
args[0] = nrf_authenticate();
} break;
+ case SYSCALL_NRF_REBOOT: {
+ nrf_reboot();
+ } break;
+
#endif
#ifdef USE_POWER_MANAGER
diff --git a/core/embed/sys/syscall/stm32/syscall_stubs.c b/core/embed/sys/syscall/stm32/syscall_stubs.c
index 77d0d313..86848de6 100644
--- a/core/embed/sys/syscall/stm32/syscall_stubs.c
+++ b/core/embed/sys/syscall/stm32/syscall_stubs.c
@@ -694,6 +694,8 @@ bool nrf_authenticate(void) {
return (bool)syscall_invoke0(SYSCALL_NRF_AUTHENTICATE);
}
+void nrf_reboot(void) { syscall_invoke0(SYSCALL_NRF_REBOOT); }
+
#endif
// =============================================================================
diff --git a/core/embed/upymod/modtrezorutils/modtrezorutils.c b/core/embed/upymod/modtrezorutils/modtrezorutils.c
index f68cfb35..c74b2b17 100644
--- a/core/embed/upymod/modtrezorutils/modtrezorutils.c
+++ b/core/embed/upymod/modtrezorutils/modtrezorutils.c
@@ -43,6 +43,9 @@
#include "blake2s.h"
#include "memzero.h"
+#ifdef USE_BLE
+#include <io/ble.h>
+#endif
#ifdef USE_NRF
#include <io/nrf.h>
#endif
@@ -523,6 +526,13 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorutils_reboot_to_bootloader_obj,
/// Reboots the device.
/// """
STATIC mp_obj_t mod_trezorutils_reboot(void) {
+#ifdef USE_BLE
+ ble_switch_off();
+#endif
+#ifdef USE_NRF
+ nrf_reboot();
+#endif
+
// Just reboot and go through the normal boot sequence
reboot_device();
Why this scored 32/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.