fix(core/ui): avoid signed overflow when calculating text width
What changed, and why it matters
This commit fixes a bug in how the Trezor hardware wallet calculates the width of on-screen text. Previously, very long text could cause an internal counter to overflow from a large positive number to a negative number, which might make text appear incorrectly sized or positioned. The fix uses saturating arithmetic so the counter stops at the maximum safe value instead of wrapping around.
Treat as a low-severity hardening fix. Review whether any callers of text_width() or related layout functions could be fed attacker-controlled long strings and whether negative widths previously caused buffer misbehavior. Consider adding an input-length or width bound for untrusted text. No urgent response required absent evidence of exploitability.
Security signals we found
Signed integer overflow in UI text measurement
Use of saturating arithmetic as defensive fix
Potential UI/layout misbehavior from negative width values
Evidence from the diff
In core/embed/rust/src/ui/display/font.rs, the text_width() method accumulated character widths into a signed i16 counter. With sufficiently long input strings, this counter could experience signed integer overflow, producing a negative width. The patch replaces the plain i16 accumulation with core::num::Saturating
Changed components
core/embed/rust/src/ui/display/font.rsFontInfo::text_width()Trezor device on-screen text rendering/layoutInspect captured patch +6 / −2
diff --git a/core/embed/rust/src/ui/display/font.rs b/core/embed/rust/src/ui/display/font.rs
index 13d1d3b9..73919dc0 100644
--- a/core/embed/rust/src/ui/display/font.rs
+++ b/core/embed/rust/src/ui/display/font.rs
@@ -1,3 +1,5 @@
+use core::num::Saturating;
+
#[cfg(feature = "translations")]
use spin::RwLockReadGuard;
@@ -242,7 +244,9 @@ fn calculate_glyph_size(header: &[u8]) -> usize {
impl FontInfo {
/// Supports UTF8 characters
pub fn text_width(&'static self, text: &str) -> i16 {
- let mut width = 0;
+ // Really long text makes width overflow into negative values.
+ // It's better to return i16::MAX in that case.
+ let mut width = Saturating(0);
let mut prev_char: Option<char> = None;
for c in text.chars() {
@@ -250,7 +254,7 @@ impl FontInfo {
width += self.char_width(c);
prev_char = Some(c);
}
- width
+ width.0
}
/// Width of the text that is visible.
Why this scored 42/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.