fix(eckhart): allow device name to overflow
What changed, and why it matters
This commit changes how the Trezor hardware wallet displays a long device name in its settings menu. Previously, if the device name was too long to fit on screen, the firmware would trigger a fatal error in debug builds. The fix allows the name to overflow visually instead of crashing. There is no indication this is a security vulnerability; it is a UI robustness fix.
No security action required. Treat as a normal UI fix. If reviewing further, verify that overflowing text is rendered with an ellipsis or clipped safely and does not overlap touch targets.
Security signals we found
UI-only change with no cryptographic, authentication, or firmware-update logic touched
Removed/conditional fatal_error in debug builds for long subtext strings
Change is defensive: prevents a debug crash from a user-controlled display string (device name)
Evidence from the diff
The patch adds a new Button variant and constructor that permit subtext to overflow its allocated width, and uses it for the device name entry in the Eckhart layout’s device menu. It introduces a subtext_overflow: bool flag in ButtonContent::TextAndSubtext and MenuItem, plus new_menu_item_with_overflowing_subtext and with_text_and_overflowing_subtext helpers. The debug-only fatal_error check in Button::paint is now skipped when subtext_overflow is true. The device name menu item is switched to use the overflowing variant.
Changed components
core/embed/rust/src/ui/layout_eckhart/component/button.rscore/embed/rust/src/ui/layout_eckhart/firmware/device_menu_screen.rsInspect captured patch +63 / −14
diff --git a/core/embed/rust/src/ui/layout_eckhart/component/button.rs b/core/embed/rust/src/ui/layout_eckhart/component/button.rs
index 2a5d44e2..771768fe 100644
--- a/core/embed/rust/src/ui/layout_eckhart/component/button.rs
+++ b/core/embed/rust/src/ui/layout_eckhart/component/button.rs
@@ -104,6 +104,19 @@ impl Button {
.with_radius(Self::MENU_ITEM_RADIUS)
}
+ pub fn new_menu_item_with_overflowing_subtext(
+ text: TString<'static>,
+ stylesheet: ButtonStyleSheet,
+ subtext: TString<'static>,
+ subtext_style: &'static TextStyle,
+ ) -> Self {
+ Self::with_text_and_overflowing_subtext(text, subtext, subtext_style, None)
+ .with_text_align(Self::MENU_ITEM_ALIGNMENT)
+ .with_content_offset(Self::MENU_ITEM_CONTENT_OFFSET)
+ .styled(stylesheet)
+ .with_radius(Self::MENU_ITEM_RADIUS)
+ }
+
#[cfg(feature = "micropython")]
pub fn new_connection_item(
text: TString<'static>,
@@ -155,6 +168,22 @@ impl Button {
text,
subtext,
subtext_style,
+ subtext_overflow: false,
+ icon,
+ })
+ }
+
+ pub fn with_text_and_overflowing_subtext(
+ text: TString<'static>,
+ subtext: TString<'static>,
+ subtext_style: &'static TextStyle,
+ icon: Option<(Icon, Color)>,
+ ) -> Self {
+ Self::new(ButtonContent::TextAndSubtext {
+ text,
+ subtext,
+ subtext_style,
+ subtext_overflow: true,
icon,
})
}
@@ -328,12 +357,7 @@ impl Button {
text.map(|t| self.text_height(t, *single_line, width))
}
ButtonContent::Icon(icon) => icon.toif.height(),
- ButtonContent::TextAndSubtext {
- text,
- subtext: _,
- subtext_style: _,
- icon,
- } => {
+ ButtonContent::TextAndSubtext { text, icon, .. } => {
let width = if icon.is_some() {
width - Self::CONN_ICON_WIDTH
} else {
@@ -464,6 +488,7 @@ impl Button {
text,
subtext,
subtext_style,
+ subtext_overflow,
icon,
} => {
let text_baseline_height = self.baseline_text_height();
@@ -494,7 +519,9 @@ impl Button {
subtext.map(|subtext| {
#[cfg(feature = "ui_debug")]
{
- if subtext_style.text_font.text_width(subtext) > available_width {
+ if subtext_style.text_font.text_width(subtext) > available_width
+ && !subtext_overflow
+ {
fatal_error!(&uformat!(len: 128, "Subtext too long: '{}'", subtext));
}
}
@@ -726,6 +753,7 @@ pub enum ButtonContent {
text: TString<'static>,
subtext: TString<'static>,
subtext_style: &'static TextStyle,
+ subtext_overflow: bool,
icon: Option<(Icon, Color)>,
},
Icon(Icon),
diff --git a/core/embed/rust/src/ui/layout_eckhart/firmware/device_menu_screen.rs b/core/embed/rust/src/ui/layout_eckhart/firmware/device_menu_screen.rs
index 45e58f54..0a2c00db 100644
--- a/core/embed/rust/src/ui/layout_eckhart/firmware/device_menu_screen.rs
+++ b/core/embed/rust/src/ui/layout_eckhart/firmware/device_menu_screen.rs
@@ -97,6 +97,7 @@ pub enum DeviceMenuMsg {
struct MenuItem {
text: TString<'static>,
subtext: Option<(TString<'static>, Option<&'static TextStyle>)>,
+ subtext_overflow: bool,
stylesheet: &'static ButtonStyleSheet,
connection_status: Option<bool>,
action: Option<Action>,
@@ -111,6 +112,7 @@ impl MenuItem {
Self {
text,
subtext: None,
+ subtext_overflow: false,
stylesheet: MENU_ITEM_NORMAL,
action,
connection_status: None,
@@ -122,6 +124,16 @@ impl MenuItem {
subtext: Option<(TString<'static>, Option<&'static TextStyle>)>,
) -> &mut Self {
self.subtext = subtext;
+ self.subtext_overflow = false;
+ self
+ }
+
+ pub fn with_overflowing_subtext(
+ &mut self,
+ subtext: Option<(TString<'static>, Option<&'static TextStyle>)>,
+ ) -> &mut Self {
+ self.subtext = subtext;
+ self.subtext_overflow = true;
self
}
@@ -465,7 +477,7 @@ impl DeviceMenuScreen {
TR::words__name.into(),
Some(Action::Return(DeviceMenuMsg::DeviceName)),
);
- item_device_name.with_subtext(Some((device_name, None)));
+ item_device_name.with_overflowing_subtext(Some((device_name, None)));
unwrap!(items.push(item_device_name));
}
@@ -606,12 +618,21 @@ impl DeviceMenuScreen {
} else if let Some((subtext, subtext_style)) = item.subtext {
let subtext_style =
subtext_style.unwrap_or(&theme::TEXT_MENU_ITEM_SUBTITLE);
- Button::new_menu_item_with_subtext(
- item.text,
- *item.stylesheet,
- subtext,
- subtext_style,
- )
+ if item.subtext_overflow {
+ Button::new_menu_item_with_overflowing_subtext(
+ item.text,
+ *item.stylesheet,
+ subtext,
+ subtext_style,
+ )
+ } else {
+ Button::new_menu_item_with_subtext(
+ item.text,
+ *item.stylesheet,
+ subtext,
+ subtext_style,
+ )
+ }
} else {
Button::new_menu_item(item.text, *item.stylesheet)
};
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.