What changed, and why it matters
This commit adds a standard Bluetooth 'battery service' that lets a connected phone or computer read the device's battery percentage. The feature itself is normal and not a security bug. The only small concern is that the new command accepts a battery level byte without checking the message length, but because the value is immediately passed to Zephyr's standard battery-service helper, there is no clear way to misuse it. There is no evidence this was reported as a security issue.
No security action required. As a defensive hardening measure, the Nordic process_command handler could add a length check (len >= 2) before indexing data[1] in the INTERNAL_CMD_BATTERY_UPDATE case.
Security signals we found
New inter-processor command INTERNAL_CMD_BATTERY_UPDATE added without explicit input-length validation on Nordic receiver
Battery state-of-charge value is forwarded from power manager to standard Zephyr BAS service
No changelog entry, but commit title explicitly describes feature addition
Evidence from the diff
The patch wires the STM32 main MCU to the Nordic BLE controller with a new INTERNAL_CMD_BATTERY_UPDATE (0x0F) message. The STM32 side reads pm_get_state().soc and sends it; the Nordic side calls bt_bas_set_battery_level(data[1]). It enables CONFIG_BT_BAS in the Zephyr project config. The command handler does not validate len >= 2 before indexing data[1], but the message is fixed-size on the sender side and the Zephyr BAS API simply clamps/uses the u8_t value, so no buffer overflow or out-of-range behavior is evident. No changelog entry is present, which is consistent with a feature addition marked [no changelog].
Changed components
core/embed/io/ble/stm32/ble.ccore/embed/io/ble/stm32/ble_comm_defs.hnordic/trezor/trezor-ble/prj.confnordic/trezor/trezor-ble/src/ble/ble_internal.hnordic/trezor/trezor-ble/src/ble/ble_management.cInspect captured patch +41 / −0
diff --git a/core/embed/io/ble/stm32/ble.c b/core/embed/io/ble/stm32/ble.c
index 749b7f7cd..0b9cd65dc 100644
--- a/core/embed/io/ble/stm32/ble.c
+++ b/core/embed/io/ble/stm32/ble.c
@@ -34,6 +34,10 @@
#include <util/tsqueue.h>
#include <util/unit_properties.h>
+#ifdef USE_POWER_MANAGER
+#include <sys/power_manager.h>
+#endif
+
#include "ble_comm_defs.h"
static bool ble_start_pairing(ble_command_t *command);
@@ -91,6 +95,12 @@ typedef struct {
systimer_t *timer;
uint16_t ping_cntr;
+
+#ifdef USE_POWER_MANAGER
+ uint8_t soc;
+ bool soc_send;
+#endif
+
} ble_driver_t;
static ble_driver_t g_ble_driver = {0};
@@ -206,6 +216,13 @@ static bool ble_send_bond_list_request(ble_driver_t *drv) {
return nrf_send_msg(NRF_SERVICE_BLE_MANAGER, &cmd, sizeof(cmd), NULL, NULL);
}
+#ifdef USE_POWER_MANAGER
+static bool ble_send_battery_update(uint8_t level) {
+ uint8_t cmd[2] = {INTERNAL_CMD_BATTERY_UPDATE, level};
+
+ return nrf_send_msg(NRF_SERVICE_BLE_MANAGER, cmd, sizeof(cmd), NULL, NULL);
+}
+#endif
static void ble_process_rx_msg_status(const uint8_t *data, uint32_t len) {
ble_driver_t *drv = &g_ble_driver;
@@ -568,6 +585,18 @@ static void ble_loop(void *context) {
ble_send_advertising_on(drv, false);
}
}
+
+#ifdef USE_POWER_MANAGER
+ pm_state_t state = {0};
+ if (PM_OK == pm_get_state(&state)) {
+ if (state.soc != drv->soc || !drv->soc_send) {
+ ble_send_battery_update(state.soc);
+ drv->soc = state.soc;
+ drv->soc_send = true;
+ }
+ }
+#endif
+
} else {
drv->status_valid = false;
}
diff --git a/core/embed/io/ble/stm32/ble_comm_defs.h b/core/embed/io/ble/stm32/ble_comm_defs.h
index d0d4da26e..9436b6322 100644
--- a/core/embed/io/ble/stm32/ble_comm_defs.h
+++ b/core/embed/io/ble/stm32/ble_comm_defs.h
@@ -75,6 +75,7 @@ typedef enum {
INTERNAL_CMD_SET_SPEED_HIGH = 0x0C,
INTERNAL_CMD_SET_SPEED_LOW = 0x0D,
INTERNAL_CMD_NOTIFY = 0x0E,
+ INTERNAL_CMD_BATTERY_UPDATE = 0x0F,
} internal_cmd_t;
typedef struct {
diff --git a/nordic/trezor/trezor-ble/prj.conf b/nordic/trezor/trezor-ble/prj.conf
index f54712f8a..662762256 100644
--- a/nordic/trezor/trezor-ble/prj.conf
+++ b/nordic/trezor/trezor-ble/prj.conf
@@ -59,6 +59,7 @@ CONFIG_BT_PERIPHERAL_PREF_MAX_INT=100
CONFIG_BT_PERIPHERAL_PREF_TIMEOUT=400
CONFIG_BT_CTLR_PHY_2M=y
CONFIG_BT_USER_PHY_UPDATE=y
+CONFIG_BT_BAS=y
CONFIG_BT_DIS=y
CONFIG_BT_DIS_MANUF="Trezor Company s.r.o"
diff --git a/nordic/trezor/trezor-ble/src/ble/ble_internal.h b/nordic/trezor/trezor-ble/src/ble/ble_internal.h
index d63b3aacc..bfdb28f4a 100644
--- a/nordic/trezor/trezor-ble/src/ble/ble_internal.h
+++ b/nordic/trezor/trezor-ble/src/ble/ble_internal.h
@@ -111,6 +111,7 @@ typedef enum {
INTERNAL_CMD_SET_SPEED_HIGH = 0x0C,
INTERNAL_CMD_SET_SPEED_LOW = 0x0D,
INTERNAL_CMD_NOTIFY = 0x0E,
+ INTERNAL_CMD_BATTERY_UPDATE = 0x0F,
} internal_cmd_t;
typedef struct {
diff --git a/nordic/trezor/trezor-ble/src/ble/ble_management.c b/nordic/trezor/trezor-ble/src/ble/ble_management.c
index 093d42ff4..14826e6bf 100644
--- a/nordic/trezor/trezor-ble/src/ble/ble_management.c
+++ b/nordic/trezor/trezor-ble/src/ble/ble_management.c
@@ -23,6 +23,7 @@
#include <zephyr/kernel.h>
#include <zephyr/types.h>
+#include <zephyr/bluetooth/services/bas.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/crc.h>
@@ -144,6 +145,10 @@ static void management_send_bonds(void) {
sizeof(tx_data));
}
+static void management_update_battery(uint8_t level) {
+ bt_bas_set_battery_level(level);
+}
+
static void process_command(uint8_t *data, uint16_t len) {
uint8_t cmd = data[0];
bool success = true;
@@ -218,6 +223,10 @@ static void process_command(uint8_t *data, uint16_t len) {
service_notify(conn, &data[1], len - 1);
}
} break;
+ case INTERNAL_CMD_BATTERY_UPDATE: {
+ management_update_battery(data[1]);
+ } break;
+ ;
default:
break;
}
Why this scored 24/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.