feat(core/prodtest): prevent production prodtest from erasing of backup RAM
What changed, and why it matters
This commit makes two changes to Trezor's firmware. First, it removes the ability of the production-test tool ('prodtest') to erase a special memory area called 'backup RAM' when the device is built for real production use. Second, it replaces a secure-monitor call that only returned battery temperature min/max values with one that returns a broader 'telemetry data' structure. The commit title frames the change as preventing production prodtest from erasing backup RAM, which suggests a hardening measure rather than a reported vulnerability fix.
Treat as a hardening commit. Review whether the new telemetry_data_t structure contains any sensitive fields beyond battery temperature, and confirm the secure-world implementation of telemetry_get() does not leak secrets. Verify that production builds correctly set PRODUCTION=1 so the prodtest erase command is excluded. No immediate user action is indicated by the commit alone.
Security signals we found
Hardening: production prodtest can no longer erase backup RAM
Potential information disclosure expansion: telemetry SMCALL now returns a full structure rather than just min/max battery temperature
Secure-monitor verifier updated to validate write access to the new telemetry output structure
No changelog entry provided ([no changelog])
No CVE, advisory, or researcher attribution in commit
Evidence from the diff
The diff modifies six files. In prodtest_backup_ram.c, the erase command is now guarded by #if !PRODUCTION, while the read command remains non-production only. In the SMCALL (secure monitor call) layer, SMCALL_TELEMETRY_GET_BATT_TEMP_MIN_MAX is renamed/replaced by SMCALL_TELEMETRY_GET, and the dispatch, stubs, and verifiers are updated to pass a telemetry_data_t pointer instead of two float pointers. The verifier checks write access to the whole telemetry_data_t object before calling into the secure world. No runtime logic for what telemetry_get returns is shown in the diff, and no changelog entry is provided.
Changed components
core/embed/projects/prodtest/cmd/prodtest_backup_ram.ccore/embed/sys/smcall/stm32/smcall_dispatch.ccore/embed/sys/smcall/stm32/smcall_numbers.hcore/embed/sys/smcall/stm32/smcall_stubs.ccore/embed/sys/smcall/stm32/smcall_verifiers.ccore/embed/sys/smcall/stm32/smcall_verifiers.hInspect captured patch +13 / −20
diff --git a/core/embed/projects/prodtest/cmd/prodtest_backup_ram.c b/core/embed/projects/prodtest/cmd/prodtest_backup_ram.c
index 5c49184b6..369e41cce 100644
--- a/core/embed/projects/prodtest/cmd/prodtest_backup_ram.c
+++ b/core/embed/projects/prodtest/cmd/prodtest_backup_ram.c
@@ -59,6 +59,8 @@ static void prodtest_backup_ram_list(cli_t* cli) {
cli_ok(cli, "");
}
+#if !PRODUCTION
+
static void prodtest_backup_ram_erase(cli_t* cli) {
if (cli_arg_count(cli) > 0) {
cli_error_arg_count(cli);
@@ -75,8 +77,6 @@ static void prodtest_backup_ram_erase(cli_t* cli) {
cli_ok(cli, "");
}
-#if !PRODUCTION
-
static void prodtest_backup_ram_read(cli_t* cli) {
if (cli_arg_count(cli) != 1) {
cli_error_arg_count(cli);
diff --git a/core/embed/sys/smcall/stm32/smcall_dispatch.c b/core/embed/sys/smcall/stm32/smcall_dispatch.c
index 22c1bf5a7..f3f6ffe15 100644
--- a/core/embed/sys/smcall/stm32/smcall_dispatch.c
+++ b/core/embed/sys/smcall/stm32/smcall_dispatch.c
@@ -416,11 +416,9 @@ __attribute((no_stack_protector)) void smcall_handler(uint32_t *args,
telemetry_update_battery_temp__verified(temp);
} break;
- case SMCALL_TELEMETRY_GET_BATT_TEMP_MIN_MAX: {
- float *out_min_c = (float *)args[0];
- float *out_max_c = (float *)args[1];
- args[0] =
- telemetry_get_battery_temp_min_max__verified(out_min_c, out_max_c);
+ case SMCALL_TELEMETRY_GET: {
+ telemetry_data_t *out = (telemetry_data_t *)args[0];
+ args[0] = telemetry_get__verified(out);
} break;
#endif // USE_TELEMETRY
diff --git a/core/embed/sys/smcall/stm32/smcall_numbers.h b/core/embed/sys/smcall/stm32/smcall_numbers.h
index 5fc3039ae..8e53b4618 100644
--- a/core/embed/sys/smcall/stm32/smcall_numbers.h
+++ b/core/embed/sys/smcall/stm32/smcall_numbers.h
@@ -105,6 +105,6 @@ typedef enum {
SMCALL_SECRET_KEYS_GET_DELEGATED_IDENTITY_KEY,
SMCALL_TELEMETRY_UPDATE_BATT_TEMP,
- SMCALL_TELEMETRY_GET_BATT_TEMP_MIN_MAX,
+ SMCALL_TELEMETRY_GET,
} smcall_number_t;
diff --git a/core/embed/sys/smcall/stm32/smcall_stubs.c b/core/embed/sys/smcall/stm32/smcall_stubs.c
index 2f854eedf..220245a0d 100644
--- a/core/embed/sys/smcall/stm32/smcall_stubs.c
+++ b/core/embed/sys/smcall/stm32/smcall_stubs.c
@@ -424,9 +424,8 @@ void telemetry_update_battery_temp(float temp_c) {
smcall_invoke1(float_to_u32.u, SMCALL_TELEMETRY_UPDATE_BATT_TEMP);
}
-bool telemetry_get_battery_temp_min_max(float *out_min_c, float *out_max_c) {
- return (bool)smcall_invoke2((uint32_t)out_min_c, (uint32_t)out_max_c,
- SMCALL_TELEMETRY_GET_BATT_TEMP_MIN_MAX);
+bool telemetry_get(telemetry_data_t *out) {
+ return (bool)smcall_invoke1((uint32_t)out, SMCALL_TELEMETRY_GET);
}
#endif
diff --git a/core/embed/sys/smcall/stm32/smcall_verifiers.c b/core/embed/sys/smcall/stm32/smcall_verifiers.c
index 96872a5cc..a6563ff15 100644
--- a/core/embed/sys/smcall/stm32/smcall_verifiers.c
+++ b/core/embed/sys/smcall/stm32/smcall_verifiers.c
@@ -583,16 +583,12 @@ void telemetry_update_battery_temp__verified(float temp_c) {
telemetry_update_battery_temp(temp_c);
}
-bool telemetry_get_battery_temp_min_max__verified(float *out_min_c,
- float *out_max_c) {
- if (out_min_c && !probe_write_access(out_min_c, sizeof(*out_min_c))) {
- goto access_violation;
- }
- if (out_max_c && !probe_write_access(out_max_c, sizeof(*out_max_c))) {
+bool telemetry_get__verified(telemetry_data_t *out) {
+ if (out != NULL && !probe_write_access(out, sizeof(*out))) {
goto access_violation;
}
- return telemetry_get_battery_temp_min_max(out_min_c, out_max_c);
+ return telemetry_get(out);
access_violation:
apptask_access_violation();
diff --git a/core/embed/sys/smcall/stm32/smcall_verifiers.h b/core/embed/sys/smcall/stm32/smcall_verifiers.h
index bda3552d1..d627b6c47 100644
--- a/core/embed/sys/smcall/stm32/smcall_verifiers.h
+++ b/core/embed/sys/smcall/stm32/smcall_verifiers.h
@@ -173,8 +173,8 @@ secbool secret_validate_nrf_pairing__verified(const uint8_t *message,
void telemetry_update_battery_temp__verified(float temp_c);
-bool telemetry_get_battery_temp_min_max__verified(float *out_min_c,
- float *out_max_c);
+bool telemetry_get__verified(telemetry_data_t *out);
+
#endif // USE_TELEMETRY
#endif // SECMON
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.