fix(core): fix updating battery errors in telemetry
What changed, and why it matters
This commit fixes how the Trezor hardware wallet reports battery-related warning flags to its internal telemetry system. Previously, the code reported the current state of several battery conditions on every check (for example, 'battery not connected right now'). After the fix, it only reports when the condition actually changes or is newly detected. This is a correctness bug in telemetry logging, not a vulnerability that could let an attacker steal funds or control the device.
No security action required. Treat as a normal firmware maintenance fix. If an advisory or CVE is later published by the vendor or a researcher, re-evaluate based on that new evidence.
Security signals we found
Telemetry state-reporting logic corrected to use event transitions instead of absolute state values
No change to cryptographic, USB, display, or secret-handling code paths
No input parsing, buffer handling, or privilege boundary changes
Commit title and message describe the change as a telemetry fix with no changelog entry
Evidence from the diff
The patch changes core/embed/io/power_manager/power_manager_poll.c so that telemetry_batt_errors_t is populated from transition/event flags rather than from absolute state values. Before, ntc_disconnected, charging_limited, battery_disconnected, and temp_control_active were set unconditionally based on new_state, and battery_ocv_jump_detected/battery_temp_jump_detected were set from already-cleared event flags. After the patch, each error bit is set only when the corresponding FSM event flag is newly raised (state change or jump detection), and telemetry_update_battery_errors() is called once with the accumulated bitmask. This corrects telemetry semantics but does not alter power-management decisions or expose attacker-controlled data paths.
Changed components
core/embed/io/power_manager/power_manager_poll.cTrezor Safe hardware power-management telemetry subsystemInspect captured patch +34 / −9
diff --git a/core/embed/io/power_manager/power_manager_poll.c b/core/embed/io/power_manager/power_manager_poll.c
index 20df9c60..2906a6f6 100644
--- a/core/embed/io/power_manager/power_manager_poll.c
+++ b/core/embed/io/power_manager/power_manager_poll.c
@@ -119,12 +119,19 @@ static bool pm_fsm_update(pm_fsm_t* fsm, pm_state_t* new_state) {
fsm->events.flags.soc_updated = true;
}
+#ifdef USE_TELEMETRY
+ telemetry_batt_errors_t errors = {0};
+#endif
+
// Detect battery temperature jump
const float TEMP_JUMP_THRESHOLD_C = 5.0f;
const uint32_t TEMP_JUMP_WINDOW_MS = 5000; // 5 seconds
if (pm_detect_jump(&fsm->temp_detector, new_state->battery_temp,
TEMP_JUMP_THRESHOLD_C, TEMP_JUMP_WINDOW_MS)) {
fsm->events.flags.battery_temp_jump_detected = true;
+#ifdef USE_TELEMETRY
+ errors.bits.battery_temp_jump_detected = true;
+#endif
}
// Detect battery OCV jump
@@ -133,6 +140,9 @@ static bool pm_fsm_update(pm_fsm_t* fsm, pm_state_t* new_state) {
if (pm_detect_jump(&fsm->ocv_detector, new_state->battery_ocv,
OCV_JUMP_THRESHOLD_V, OCV_JUMP_WINDOW_MS)) {
fsm->events.flags.battery_ocv_jump_detected = true;
+#ifdef USE_TELEMETRY
+ errors.bits.battery_ocv_jump_detected = true;
+#endif
}
if (new_state->usb_connected != fsm->last_state.usb_connected) {
@@ -153,26 +163,41 @@ static bool pm_fsm_update(pm_fsm_t* fsm, pm_state_t* new_state) {
if (new_state->ntc_connected != fsm->last_state.ntc_connected) {
fsm->events.flags.ntc_connected_changed = true;
+#ifdef USE_TELEMETRY
+ if (!new_state->ntc_connected) {
+ errors.bits.ntc_disconnected = true;
+ }
+#endif
}
if (new_state->charging_limited != fsm->last_state.charging_limited) {
fsm->events.flags.charging_limited_changed = true;
+#ifdef USE_TELEMETRY
+ if (new_state->charging_limited) {
+ errors.bits.charging_limited = true;
+ }
+#endif
}
if (new_state->battery_connected != fsm->last_state.battery_connected) {
fsm->events.flags.battery_connected_changed = true;
+#ifdef USE_TELEMETRY
+ if (!new_state->battery_connected) {
+ errors.bits.battery_disconnected = true;
+ }
+#endif
+ }
+
+ if (new_state->temp_control_active != fsm->last_state.temp_control_active) {
+ fsm->events.flags.temp_control_active_changed = true;
+#ifdef USE_TELEMETRY
+ if (new_state->temp_control_active) {
+ errors.bits.temp_control_active = true;
+ }
+#endif
}
#ifdef USE_TELEMETRY
- telemetry_batt_errors_t errors = {0};
- errors.bits.ntc_disconnected = !new_state->ntc_connected;
- errors.bits.charging_limited = new_state->charging_limited;
- errors.bits.battery_disconnected = !new_state->battery_connected;
- errors.bits.temp_control_active = new_state->temp_control_active;
- errors.bits.battery_ocv_jump_detected =
- fsm->events.flags.battery_ocv_jump_detected;
- errors.bits.battery_temp_jump_detected =
- fsm->events.flags.battery_temp_jump_detected;
telemetry_update_battery_errors(errors);
#endif
Why this scored 17/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.