build(core): replace -fsingle-precision-constant with explicit casts
What changed, and why it matters
This is a build-system cleanup for the Trezor hardware wallet firmware. The developers replaced a GCC-only compiler flag with portable warning flags and added explicit type casts in a handful of driver files to silence the new warnings. The changes are almost entirely about float/double precision and integer casts in display, power-management, touch-simulation, and telemetry code. There is no indication this fixes an active security vulnerability; it is a maintainability and portability improvement.
No security action required. Treat as normal code-quality/portability commit. Reviewers may verify that the explicit casts preserve the original intended values, particularly the DSI timing and battery telemetry conversions, but no incident response is warranted.
Security signals we found
No security-relevant signals in commit message or diff
Changes are build/compiler-warning hygiene, not vulnerability remediation
Explicit casts reduce ambiguity but do not change intended arithmetic semantics
No input validation, memory safety, or cryptographic changes present
Evidence from the diff
Commit 03e661ce removes -fsingle-precision-constant (unsupported in clang) from core/embed/models/build.rs and adds -Wdouble-promotion and -Wfloat-conversion. It then fixes the resulting warnings by: appending f suffixes to floating-point literals; adding explicit (uint32_t), (uint16_t), (int), and (int32_t) casts in display DSI timing, SDL Unix display/touch drivers, power-monitoring calculations, and MicroPython telemetry. The functional behavior is intended to remain identical; the patch makes implicit conversions explicit so the code compiles cleanly under the new warnings.
Changed components
core/embed/models/build.rs (build flags)core/embed/io/display/ltdc_dsi/display_driver.ccore/embed/io/display/unix/display_driver.ccore/embed/io/power_manager/npm1300/npm1300.ccore/embed/io/power_manager/stm32u5/power_monitoring.ccore/embed/io/power_manager/stwlc38/stwlc38.ccore/embed/io/touch/unix/touch.ccore/embed/upymod/modtrezorutils/modtrezorutils.cInspect captured patch +40 / −35
diff --git a/core/embed/io/display/ltdc_dsi/display_driver.c b/core/embed/io/display/ltdc_dsi/display_driver.c
index ea13c6a1..00c9f4e1 100644
--- a/core/embed/io/display/ltdc_dsi/display_driver.c
+++ b/core/embed/io/display/ltdc_dsi/display_driver.c
@@ -160,10 +160,12 @@ static bool display_dsi_init(display_driver_t *drv) {
drv->DSIVidCfg.PacketSize = HACT;
drv->DSIVidCfg.NumberOfChunks = 0; // No chunks in burst mode
drv->DSIVidCfg.NullPacketSize = 0; // No null packet in burst mode
- drv->DSIVidCfg.HorizontalSyncActive = HSYNC * LANE_BYTE_2_PIXEL_CLK_RATIO;
- drv->DSIVidCfg.HorizontalBackPorch = HBP * LANE_BYTE_2_PIXEL_CLK_RATIO;
+ drv->DSIVidCfg.HorizontalSyncActive =
+ (uint32_t)(HSYNC * LANE_BYTE_2_PIXEL_CLK_RATIO);
+ drv->DSIVidCfg.HorizontalBackPorch =
+ (uint32_t)(HBP * LANE_BYTE_2_PIXEL_CLK_RATIO);
drv->DSIVidCfg.HorizontalLine =
- (HSYNC + HBP + HACT + HFP) * LANE_BYTE_2_PIXEL_CLK_RATIO;
+ (uint32_t)((HSYNC + HBP + HACT + HFP) * LANE_BYTE_2_PIXEL_CLK_RATIO);
drv->DSIVidCfg.VerticalSyncActive = VSYNC;
drv->DSIVidCfg.VerticalBackPorch = VBP;
drv->DSIVidCfg.VerticalFrontPorch = VFP;
diff --git a/core/embed/io/display/unix/display_driver.c b/core/embed/io/display/unix/display_driver.c
index 0eb641ba..4f33d668 100644
--- a/core/embed/io/display/unix/display_driver.c
+++ b/core/embed/io/display/unix/display_driver.c
@@ -681,7 +681,7 @@ static void display_draw_suspend_overlay_internal(void) {
// create a blue texture
SDL_Texture *overlay =
SDL_CreateTexture(drv->renderer, SDL_PIXELFORMAT_RGBA8888,
- SDL_TEXTUREACCESS_STATIC, screen.w, screen.h);
+ SDL_TEXTUREACCESS_STATIC, (int)screen.w, (int)screen.h);
SDL_SetTextureBlendMode(overlay, SDL_BLENDMODE_BLEND);
SDL_SetRenderTarget(drv->renderer, overlay);
diff --git a/core/embed/io/power_manager/npm1300/npm1300.c b/core/embed/io/power_manager/npm1300/npm1300.c
index 267e6393..69052803 100644
--- a/core/embed/io/power_manager/npm1300/npm1300.c
+++ b/core/embed/io/power_manager/npm1300/npm1300.c
@@ -46,13 +46,13 @@
#define NPM1300_ADC_READOUT_DELAY 80
// Minimum temperature that counts as valid data
-#define NPM1300_NTC_TEMP_VALID_MIN (-80.0)
+#define NPM1300_NTC_TEMP_VALID_MIN (-80.0f)
// Minimum temperature that counts as valid data
-#define NPM1300_NTC_TEMP_VALID_MAX (100.0)
+#define NPM1300_NTC_TEMP_VALID_MAX (100.0f)
// Minimum battery voltage that counts as valid data
-#define NPM1300_BATT_VOLTAGE_VALID_MIN (0.5)
+#define NPM1300_BATT_VOLTAGE_VALID_MIN (0.5f)
// NPM1300 FSM states
typedef enum {
@@ -685,16 +685,16 @@ static void npm1300_calculate_report(npm1300_driver_t* drv,
// If charging, use the charge current limit (i_charge).
// See the NPM1300 datasheet for details.
if (ibat_discharging) {
- report->ibat = ((int)ibat_adc * drv->i_limit) / 1250.0;
+ report->ibat = ((int)ibat_adc * drv->i_limit) / 1250.0f;
} else if (ibat_charging) {
- report->ibat = -((int)ibat_adc * drv->i_charge) / 800.0;
+ report->ibat = -((int)ibat_adc * drv->i_charge) / 800.0f;
} else {
report->ibat = 0;
}
// Calculate the battery voltage (VBAT) from the ADC value.
// VBAT is scaled by the voltage divider ratio and ADC resolution.
- report->vbat = (vbat_adc * 5.0) / 1023.0;
+ report->vbat = (vbat_adc * 5.0f) / 1023.0f;
// if the battery voltage is below the accepted minimum, flag the battery as
// disconnected
@@ -704,10 +704,10 @@ static void npm1300_calculate_report(npm1300_driver_t* drv,
// Calculate the temperature from the NTC (thermistor).
// Beta value for the thermistor is specified as 3380.
// The equation is derived from the NPM1300 datasheet.
- float beta = 3380;
+ float beta = 3380.0f;
report->ntc_temp =
- 1 / (1 / 298.15 - (1 / beta) * logf(1024.0 / ntc_adc - 1)) - 298.15 +
- 25.0;
+ 1 / (1 / 298.15f - (1 / beta) * logf(1024.0f / ntc_adc - 1)) - 298.15f +
+ 25.0f;
// if the temperature is below the accepted minimum, flag the NTC as
// disconnected
@@ -716,12 +716,12 @@ static void npm1300_calculate_report(npm1300_driver_t* drv,
// Calculate the die temperature from the die ADC reading.
// The equation is derived from the NPM1300 datasheet.
- report->die_temp = 394.67 - 0.7926 * die_adc;
+ report->die_temp = 394.67f - 0.7926f * die_adc;
// Calculate the system voltage (VSYS) from the ADC value.
// VSYS is scaled based on the system voltage divider ratio and ADC
// resolution.
- report->vsys = (vsys_adc * 6.375) / 1023.0;
+ report->vsys = (vsys_adc * 6.375f) / 1023.0f;
// Populate measurement and status flags from the raw data
report->ibat_meas_status = r->adc_ibat_meas_status;
diff --git a/core/embed/io/power_manager/stm32u5/power_monitoring.c b/core/embed/io/power_manager/stm32u5/power_monitoring.c
index ad1262dd..e945c919 100644
--- a/core/embed/io/power_manager/stm32u5/power_monitoring.c
+++ b/core/embed/io/power_manager/stm32u5/power_monitoring.c
@@ -46,10 +46,10 @@ static const struct {
float max_temp;
float current_limit_factor;
} temp_bands[] = {
- {PM_TEMP_CONTROL_BAND_1_MAX_TEMP, 1.0},
- {PM_TEMP_CONTROL_BAND_2_MAX_TEMP, 0.7},
- {PM_TEMP_CONTROL_BAND_3_MAX_TEMP, 0.5},
- {PM_TEMP_CONTROL_BAND_4_MAX_TEMP, 0.3},
+ {PM_TEMP_CONTROL_BAND_1_MAX_TEMP, 1.0f},
+ {PM_TEMP_CONTROL_BAND_2_MAX_TEMP, 0.7f},
+ {PM_TEMP_CONTROL_BAND_3_MAX_TEMP, 0.5f},
+ {PM_TEMP_CONTROL_BAND_4_MAX_TEMP, 0.3f},
};
#endif
@@ -232,7 +232,7 @@ void pm_charging_controller(pm_driver_t* drv) {
if (drv->target_battery_ocv_v_tau > target_ocv_voltage_v) {
// current voltage is within tight bounds of target voltage,
// we may also force SoC estimate to target value.
- if (drv->target_battery_ocv_v_tau < target_ocv_voltage_v + 0.15) {
+ if (drv->target_battery_ocv_v_tau < target_ocv_voltage_v + 0.15f) {
bat_fg_state_t fg_state;
bat_fg_get_state(&fg_state);
bat_fg_set_soc((drv->soc_target / 100.0f) - 0.0001f, fg_state.P);
@@ -275,8 +275,8 @@ static void pm_temperature_controller(pm_driver_t* drv) {
i_chg_temp_limit_ma = 0; // Default to safety limit
for (size_t i = 0; i < sizeof(temp_bands) / sizeof(temp_bands[0]); ++i) {
if (drv->pmic_data.ntc_temp < temp_bands[i].max_temp) {
- i_chg_temp_limit_ma = PM_BATTERY_CHARGING_CURRENT_MAX *
- temp_bands[i].current_limit_factor;
+ i_chg_temp_limit_ma = (uint16_t)(PM_BATTERY_CHARGING_CURRENT_MAX *
+ temp_bands[i].current_limit_factor);
break;
}
}
diff --git a/core/embed/io/power_manager/stwlc38/stwlc38.c b/core/embed/io/power_manager/stwlc38/stwlc38.c
index 8dbc96df..2db817a5 100644
--- a/core/embed/io/power_manager/stwlc38/stwlc38.c
+++ b/core/embed/io/power_manager/stwlc38/stwlc38.c
@@ -331,12 +331,12 @@ static void stwlc38_i2c_callback(void *context, i2c_packet_t *packet) {
memset(&drv->report, 0, sizeof(stwlc38_report_t));
drv->report.ready = true;
drv->report.vout_ready = drv->report_regs.status0 & 0x40;
- drv->report.vrect = drv->report_regs.vrect / 1000.0;
- drv->report.vout = drv->report_regs.vout / 1000.0;
+ drv->report.vrect = drv->report_regs.vrect / 1000.0f;
+ drv->report.vout = drv->report_regs.vout / 1000.0f;
drv->report.icur = drv->report_regs.icur;
- drv->report.tmeas = drv->report_regs.tmeas / 10.0;
+ drv->report.tmeas = drv->report_regs.tmeas / 10.0f;
drv->report.opfreq = drv->report_regs.opfreq;
- drv->report.ntc = drv->report_regs.ntc / 10.0;
+ drv->report.ntc = drv->report_regs.ntc / 10.0f;
// Just powered-up ?
if (!was_ready) {
diff --git a/core/embed/io/touch/unix/touch.c b/core/embed/io/touch/unix/touch.c
index 27624d3e..1b4899d0 100644
--- a/core/embed/io/touch/unix/touch.c
+++ b/core/embed/io/touch/unix/touch.c
@@ -82,13 +82,15 @@ static bool is_inside_display(int x, int y) {
}
static void handle_mouse_events(touch_driver_t* drv, SDL_Event* event) {
- bool inside_display = is_inside_display(event->button.x, event->button.y);
+ int button_x = (int)event->button.x;
+ int button_y = (int)event->button.y;
+ bool inside_display = is_inside_display(button_x, button_y);
switch (event->type) {
case SDL_EVENT_MOUSE_BUTTON_DOWN:
if (inside_display) {
- int x = event->button.x - sdl_touch_offset_x;
- int y = event->button.y - sdl_touch_offset_y;
+ int x = button_x - sdl_touch_offset_x;
+ int y = button_y - sdl_touch_offset_y;
drv->last_event = TOUCH_START | touch_pack_xy(x, y);
drv->state = MOUSE_DOWN_INSIDE;
}
@@ -96,9 +98,9 @@ static void handle_mouse_events(touch_driver_t* drv, SDL_Event* event) {
case SDL_EVENT_MOUSE_BUTTON_UP:
if (drv->state != IDLE) {
- int x = inside_display ? event->button.x - sdl_touch_offset_x
+ int x = inside_display ? button_x - sdl_touch_offset_x
: touch_unpack_x(drv->last_event);
- int y = inside_display ? event->button.y - sdl_touch_offset_y
+ int y = inside_display ? button_y - sdl_touch_offset_y
: touch_unpack_y(drv->last_event);
;
drv->last_event = TOUCH_END | touch_pack_xy(x, y);
@@ -109,8 +111,8 @@ static void handle_mouse_events(touch_driver_t* drv, SDL_Event* event) {
case SDL_EVENT_MOUSE_MOTION:
if (drv->state != IDLE) {
if (inside_display) {
- int x = event->motion.x - sdl_touch_offset_x;
- int y = event->motion.y - sdl_touch_offset_y;
+ int x = (int)event->motion.x - sdl_touch_offset_x;
+ int y = (int)event->motion.y - sdl_touch_offset_y;
// simulate TOUCH_START if pressed in mouse returned on visible area
if (drv->state == MOUSE_DOWN_OUTSIDE) {
drv->last_event = TOUCH_START | touch_pack_xy(x, y);
diff --git a/core/embed/models/build.rs b/core/embed/models/build.rs
index bd692e9d..0c990d23 100644
--- a/core/embed/models/build.rs
+++ b/core/embed/models/build.rs
@@ -49,8 +49,9 @@ fn main() -> Result<()> {
"-Wno-enum-conversion",
"-Wno-type-limits",
"-Wno-shift-negative-value",
+ "-Wfloat-conversion",
+ "-Wdouble-promotion",
"-fno-common",
- "-fsingle-precision-constant",
"-fdata-sections",
"-ffunction-sections",
"-g",
diff --git a/core/embed/upymod/modtrezorutils/modtrezorutils.c b/core/embed/upymod/modtrezorutils/modtrezorutils.c
index 4dcd5b5f..4bc19857 100644
--- a/core/embed/upymod/modtrezorutils/modtrezorutils.c
+++ b/core/embed/upymod/modtrezorutils/modtrezorutils.c
@@ -87,7 +87,7 @@ STATIC mp_obj_t mod_trezorutils_telemetry_get(void) {
tuple[0] = mp_obj_new_int((int32_t)(data.min_temp_c * 1000.0f));
tuple[1] = mp_obj_new_int((int32_t)(data.max_temp_c * 1000.0f));
tuple[2] = mp_obj_new_int(data.battery_errors.all);
- tuple[3] = mp_obj_new_int(data.battery_cycles * 1000.0f);
+ tuple[3] = mp_obj_new_int((int32_t)(data.battery_cycles * 1000.0f));
return mp_obj_new_tuple(4, tuple);
}
Why this scored 18/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.