refactor(core/embed): reorganize error handling
What changed, and why it matters
This commit is a code cleanup that moves how Trezor firmware handles fatal errors and shutdowns between its internal software layers. It does not add new user-facing features or change security protections. The main risk is that reorganizing low-level error paths could accidentally introduce a bug, but the diff itself does not show a vulnerability being fixed or introduced.
Treat as a normal refactor. Run the existing unit and integration tests, especially crypto and rtl test suites, to confirm that error shutdown paths still behave correctly on both emulator and device builds. No urgent security response is warranted based on this commit alone.
Security signals we found
Refactor of fatal-error and shutdown code paths
New noreturn annotations on systask_exit, systask_exit_error, systask_exit_fatal, systask_kill
Removal of duplicated test-only system_exit_error/system_exit_fatal implementations
Introduction of Rust unwrap!/ensure!/fatal_error! macros in rtl crate
Use of bindgen-generated FFI for system exit functions
Evidence from the diff
The commit refactors error-handling routines across the rtl, crypto, and sys crates. It introduces new Rust modules (error.rs, sysexit.rs, util.rs, ffi.rs) in rtl, adds C shims for test builds (error_shims.c), moves tc_fault_handler into crypto, and removes duplicated test_setup.c implementations. The legacy C functions error_shutdown/__fatal_error now delegate to system_exit_error_ex/system_exit_fatal_ex. Several functions are marked noreturn and a small Rust call site in ui_firmware.rs is adjusted to avoid a borrow-checker issue. No security bug fix or exploit mitigation is visible in the diff.
Changed components
core/embed/rtlcore/embed/cryptocore/embed/sys/taskcore/embed/rustcore/embed/rust/src/ui/layout_caesar/ui_firmware.rsInspect captured patch +408 / −322
diff --git a/core/embed/Cargo.lock b/core/embed/Cargo.lock
index 873d2b42..7aa74b11 100644
--- a/core/embed/Cargo.lock
+++ b/core/embed/Cargo.lock
@@ -711,7 +711,9 @@ checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
name = "rtl"
version = "0.0.0"
dependencies = [
+ "bindgen",
"color-eyre",
+ "cty",
"models",
"xbuild",
]
@@ -1018,6 +1020,7 @@ dependencies = [
"num-traits",
"pareen",
"qrcodegen",
+ "rtl",
"serde_json",
"spin",
"static-alloc",
diff --git a/core/embed/crypto/Cargo.toml b/core/embed/crypto/Cargo.toml
index 8ebd1480..7d07aa57 100644
--- a/core/embed/crypto/Cargo.toml
+++ b/core/embed/crypto/Cargo.toml
@@ -53,6 +53,7 @@ test = [
"models/mcu_stm32u5g",
"models/model_t3w1",
"noise",
+ "rtl/error_shims",
"secp256k1_zkp",
"sphincsplus",
"universal_fw",
diff --git a/core/embed/crypto/build.rs b/core/embed/crypto/build.rs
index 862c0ca3..3c59eb45 100644
--- a/core/embed/crypto/build.rs
+++ b/core/embed/crypto/build.rs
@@ -43,10 +43,6 @@ fn main() -> Result<()> {
add_mldsa(lib, &attrs)?;
}
- if cfg!(feature = "test") {
- lib.add_source("src/test_setup.c");
- }
-
Ok(())
})
}
@@ -65,6 +61,8 @@ fn add_crypto_base(lib: &mut CLibrary, common_attrs: &CompileAttrs) -> Result<()
lib.add_define("ED25519_NO_PRECOMP", None);
}
+ lib.add_source("src/fault_handler.c");
+
lib.add_sources_in_dir_with_attrs(
CRYPTO_PATH,
[
diff --git a/core/embed/crypto/src/fault_handler.c b/core/embed/crypto/src/fault_handler.c
new file mode 100644
index 00000000..51bfb06c
--- /dev/null
+++ b/core/embed/crypto/src/fault_handler.c
@@ -0,0 +1,28 @@
+/*
+ * This file is part of the Trezor project, https://trezor.io/
+ *
+ * Copyright (c) SatoshiLabs
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+// Provide definitions of the system exit functions so that they can be
+// called without linking the sys crate. This is needed when compiling the
+// tests for the crates that don't depend on sys, such as the crypto crate.
+
+#include <trezor_rtl.h>
+
+void tc_fault_handler(const char *message) {
+ system_exit_error(NULL, message, NULL);
+}
diff --git a/core/embed/crypto/src/test_setup.c b/core/embed/crypto/src/test_setup.c
deleted file mode 100644
index bfdb8285..00000000
--- a/core/embed/crypto/src/test_setup.c
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * This file is part of the Trezor project, https://trezor.io/
- *
- * Copyright (c) SatoshiLabs
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-// Provide definitions of the system exit functions so that they can be
-// called without linking the sys crate. This is needed when compiling the
-// tests for the crates that don't depend on sys, such as the crypto crate.
-
-#include <stdio.h>
-#include <stdlib.h>
-
-void system_exit_error(const char *title, const char *message,
- const char *footer) {
- printf("Error: %s\n", message);
- if (title != NULL && *title != '\0') {
- printf("Title: %s\n", title);
- }
-
- if (footer != NULL && *footer != '\0') {
- printf("Footer: %s\n", footer);
- }
-
- exit(1);
-}
-
-void system_exit_fatal(const char *message, const char *file, int line) {
- printf("Fatal error: %s", message);
- if (file != NULL && *file != '\0') {
- printf(" at %s:%d", file, line);
- }
- printf("\n");
-
- exit(1);
-}
diff --git a/core/embed/rtl/Cargo.toml b/core/embed/rtl/Cargo.toml
index e267e89b..d0412256 100644
--- a/core/embed/rtl/Cargo.toml
+++ b/core/embed/rtl/Cargo.toml
@@ -5,10 +5,12 @@ edition = "2024"
links = "rtl"
[build-dependencies]
+bindgen.workspace = true
color-eyre.workspace = true
xbuild.workspace = true
[dependencies]
+cty.workspace = true
models.workspace = true
[features]
@@ -21,11 +23,6 @@ emulator = ["models/emulator"]
production = ["models/production"]
sprintf = []
universal_fw = []
+error_shims = []
-test = [
- "models/model_t3w1",
- "emulator",
-]
-
-
-
+test = ["models/model_t3w1", "emulator", "error_shims"]
diff --git a/core/embed/rtl/build.rs b/core/embed/rtl/build.rs
index 08c5e5c6..78f0d9e0 100644
--- a/core/embed/rtl/build.rs
+++ b/core/embed/rtl/build.rs
@@ -24,10 +24,12 @@ fn main() -> Result<()> {
add_uzlib(lib);
- if cfg!(feature = "test") {
- lib.add_source("src/test_setup.c");
+ if cfg!(feature = "error_shims") {
+ lib.add_source("error_shims.c");
}
+ lib.add_rust_bindings(add_rust_bindings)?;
+
Ok(())
})
}
@@ -75,3 +77,12 @@ fn add_uzlib(lib: &mut xbuild::CLibrary) {
lib.add_sources_in_dir(uzlib_path, ["adler32.c", "crc32.c", "tinflate.c"]);
}
+
+fn add_rust_bindings(builder: bindgen::Builder) -> Result<bindgen::Builder> {
+ let builder = builder
+ .header("inc/rtl/sysexit.h")
+ .allowlist_function("system_exit")
+ .allowlist_function("system_exit_error_ex")
+ .allowlist_function("system_exit_fatal_ex");
+ Ok(builder)
+}
diff --git a/core/embed/rtl/error_handling.c b/core/embed/rtl/error_handling.c
index 58d5fdbe..df79248f 100644
--- a/core/embed/rtl/error_handling.c
+++ b/core/embed/rtl/error_handling.c
@@ -18,7 +18,6 @@
*/
#include <trezor_rtl.h>
-#include "fault_handler.h"
#ifndef TREZOR_EMULATOR
// Stack check guard value set in startup code.
@@ -63,11 +62,30 @@ const char *ts_string(ts_t status) {
}
}
+void system_exit_error(const char *title, const char *message,
+ const char *footer) {
+ size_t title_len = title != NULL ? strlen(title) : 0;
+ size_t message_len = message != NULL ? strlen(message) : 0;
+ size_t footer_len = footer != NULL ? strlen(footer) : 0;
+
+ system_exit_error_ex(title, title_len, message, message_len, footer,
+ footer_len);
+}
+
+void system_exit_fatal(const char *message, const char *file, int line) {
+ size_t message_len = message != NULL ? strlen(message) : 0;
+ size_t file_len = file != NULL ? strlen(file) : 0;
+ system_exit_fatal_ex(message, message_len, file, file_len, line);
+}
+
void __attribute__((noreturn)) error_shutdown_ex(const char *title,
const char *message,
const char *footer) {
- system_exit_error(title, message, footer);
- while (1);
+ size_t title_len = title != NULL ? strlen(title) : 0;
+ size_t message_len = message != NULL ? strlen(message) : 0;
+ size_t footer_len = footer != NULL ? strlen(footer) : 0;
+ system_exit_error_ex(title, title_len, message, message_len, footer,
+ footer_len);
}
void __attribute__((noreturn)) error_shutdown(const char *message) {
@@ -76,8 +94,7 @@ void __attribute__((noreturn)) error_shutdown(const char *message) {
void __attribute__((noreturn)) __fatal_error(const char *msg, const char *file,
int line) {
- system_exit_fatal(msg, file, line);
- while (1);
+ size_t msg_len = msg != NULL ? strlen(msg) : 0;
+ size_t file_len = file != NULL ? strlen(file) : 0;
+ system_exit_fatal_ex(msg, msg_len, file, file_len, line);
}
-
-void tc_fault_handler(const char *msg) { ensure(secfalse, msg); }
diff --git a/core/embed/rtl/error_shims.c b/core/embed/rtl/error_shims.c
new file mode 100644
index 00000000..39c7dec1
--- /dev/null
+++ b/core/embed/rtl/error_shims.c
@@ -0,0 +1,68 @@
+/*
+ * This file is part of the Trezor project, https://trezor.io/
+ *
+ * Copyright (c) SatoshiLabs
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+// Provide definitions of the system exit functions so that they can be
+// called without linking the sys crate. This is needed when compiling the
+// tests for the crates that don't depend on sys, such as the crypto crate.
+
+#include <rtl/sysexit.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+static void print_rust_string(const char* str, size_t len) {
+ if (str != NULL && len > 0) {
+ fwrite(str, len, 1, stdout);
+ }
+}
+
+void system_exit_error_ex(const char* title, size_t title_len,
+ const char* message, size_t message_len,
+ const char* footer, size_t footer_len) {
+ printf("====== ERROR ======\n");
+ if (title != NULL && title_len > 0) {
+ printf("Title: ");
+ print_rust_string(title, title_len);
+ printf("\n");
+ }
+ printf("Error: ");
+ print_rust_string(message, message_len);
+ printf("\n");
+ if (footer != NULL && footer_len > 0) {
+ printf("Footer: ");
+ print_rust_string(footer, footer_len);
+ printf("\n");
+ }
+ exit(1);
+}
+
+void system_exit_fatal_ex(const char* message, size_t message_len,
+ const char* file, size_t file_len, int line) {
+ printf("====== FATAL ERROR ======\n");
+ printf("Fatal error: ");
+ print_rust_string(message, message_len);
+ printf("\n");
+ if (file != NULL && file_len > 0) {
+ printf(" at ");
+ print_rust_string(file, file_len);
+ printf(":%d", line);
+ }
+ printf("\n");
+ exit(1);
+}
diff --git a/core/embed/rtl/inc/rtl/error_handling.h b/core/embed/rtl/inc/rtl/error_handling.h
index 0a3468fe..bb933ed3 100644
--- a/core/embed/rtl/inc/rtl/error_handling.h
+++ b/core/embed/rtl/inc/rtl/error_handling.h
@@ -150,6 +150,8 @@ const char *ts_string(ts_t status);
/**
* Shows an error message and shuts down the device.
*
+ * Do not use this function in new code, use `system_exit_error()` instead.
+ *
* @param title Title of the error message (defaults to
* "INTERNAL ERROR" if NULL)
* @param message Main error message (defaults to no message if NULL)
@@ -163,6 +165,8 @@ void __attribute__((noreturn)) error_shutdown_ex(const char *title,
/**
* Shows an error message and shuts down the device.
*
+ * Do not use this function in new code, use `system_exit_error()` instead.
+ *
* @param message Main error message (defaults to no message if NULL)
*/
void __attribute__((noreturn)) error_shutdown(const char *message);
diff --git a/core/embed/rtl/inc/rtl/sysexit.h b/core/embed/rtl/inc/rtl/sysexit.h
index 7fb2bb7f..db1ec1b2 100644
--- a/core/embed/rtl/inc/rtl/sysexit.h
+++ b/core/embed/rtl/inc/rtl/sysexit.h
@@ -19,31 +19,68 @@
#pragma once
+#include <stddef.h>
+
/**
- * @brief Terminates the current task with an error message.
+ * @brief Terminates the current task normally with the given exit code.
*
- * To avoid circular dependencies, this function is declared here
- * in rtl but implemented in sys/task.
+ * If the current task is the kernel task, the error handler is called with the
+ * postmortem information. If the task is not the kernel task, the task is
+ * terminated immediately and the kernel task is scheduled.
+ *
+ * @param exitcode Exit code returned by the terminating task.
+ */
+void __attribute__((noreturn)) system_exit(int exitcode);
+
+/**
+ * @brief Terminates the current task with an error message.
*
* See the notes for `system_exit` regarding the behavior of the error handler
*
- * @param title Title of the error message
- * @param message Main error message
- * @param footer Footer of the error message
+ * @param title Title of the error.
+ * @param message Main error message.
+ * @param footer Footer text for the error display.
*/
-void system_exit_error(const char *title, const char *message,
- const char *footer);
+void __attribute__((noreturn)) system_exit_error(const char* title,
+ const char* message,
+ const char* footer);
/**
- * @brief Terminates the current task with a fatal error message.
+ * @brief Like `system_exit_error`, but with explicit lengths for the strings.
*
- * To avoid circular dependencies, this function is declared here
- * in rtl but implemented in sys/task.
+ * @param title Title of the error.
+ * @param title_len Length of the title.
+ * @param message Main error message.
+ * @param message_len Length of the message.
+ * @param footer Footer text for the error display.
+ * @param footer_len Length of the footer.
+ */
+void __attribute__((noreturn)) system_exit_error_ex(
+ const char* title, size_t title_len, const char* message,
+ size_t message_len, const char* footer, size_t footer_len);
+
+/**
+ * @brief Terminates the current task with a fatal error message.
*
* See the notes for `system_exit` regarding the behavior of the error handler
*
- * @param message Fatal error message
- * @param file Source file name where the fatal error occurred
- * @param line Line number in the source file where the fatal error occurred
+ * @param message Fatal error message.
+ * @param file Source file where the fatal error occurred.
+ * @param line Line number in the source file.
+ */
+void __attribute__((noreturn)) system_exit_fatal(const char* message,
+ const char* file, int line);
+
+/**
+ * @brief Like `system_exit_fatal`, but with explicit lengths for the strings.
+ *
+ * @param message Fatal error message.
+ * @param message_len Length of the message.
+ * @param file Source file where the fatal error occurred.
+ * @param file_len Length of the file string.
+ * @param line Line number in the source file.
*/
-void system_exit_fatal(const char *message, const char *file, int line);
+void __attribute__((noreturn)) system_exit_fatal_ex(const char* message,
+ size_t message_len,
+ const char* file,
+ size_t file_len, int line);
diff --git a/core/embed/rtl/src/error.rs b/core/embed/rtl/src/error.rs
new file mode 100644
index 00000000..9e21d8bf
--- /dev/null
+++ b/core/embed/rtl/src/error.rs
@@ -0,0 +1,52 @@
+pub trait UnwrapOrFatalError<T> {
+ fn unwrap_or_fatal_error(self, msg: &str, file: &str, line: u32) -> T;
+}
+
+impl<T> UnwrapOrFatalError<T> for Option<T> {
+ fn unwrap_or_fatal_error(self, msg: &str, file: &str, line: u32) -> T {
+ match self {
+ Some(x) => x,
+ None => crate::sysexit::system_exit_fatal(msg, file, line),
+ }
+ }
+}
+
+impl<T, E> UnwrapOrFatalError<T> for Result<T, E> {
+ fn unwrap_or_fatal_error(self, msg: &str, file: &str, line: u32) -> T {
+ match self {
+ Ok(x) => x,
+ Err(_) => crate::sysexit::system_exit_fatal(msg, file, line),
+ }
+ }
+}
+
+#[macro_export]
+macro_rules! unwrap {
+ ($e:expr, $msg:expr) => {{
+ use $crate::error::UnwrapOrFatalError;
+ $e.unwrap_or_fatal_error($msg, file!(), line!())
+ }};
+ ($expr:expr) => {
+ unwrap!($expr, "unwrap failed")
+ };
+}
+
+#[macro_export]
+macro_rules! ensure {
+ ($what:expr, $error:expr) => {
+ if !($what) {
+ $crate::sysexit::system_exit_fatal($error, file!(), line!());
+ }
+ };
+}
+
+#[macro_export]
+macro_rules! fatal_error {
+ ($msg:expr) => {{
+ $crate::sysexit::system_exit_fatal($msg, file!(), line!());
+ }};
+}
+
+pub use ensure;
+pub use fatal_error;
+pub use unwrap;
diff --git a/core/embed/rtl/src/ffi.rs b/core/embed/rtl/src/ffi.rs
new file mode 100644
index 00000000..18c45a81
--- /dev/null
+++ b/core/embed/rtl/src/ffi.rs
@@ -0,0 +1 @@
+include!(concat!(env!("OUT_DIR"), "/rtl.rs"));
diff --git a/core/embed/rtl/src/lib.rs b/core/embed/rtl/src/lib.rs
index 0c9ac1ac..604a7d65 100644
--- a/core/embed/rtl/src/lib.rs
+++ b/core/embed/rtl/src/lib.rs
@@ -1 +1,9 @@
#![no_std]
+
+mod ffi;
+
+pub mod error;
+pub mod sysexit;
+pub mod util;
+
+pub use sysexit::{system_exit_error, system_exit_fatal};
diff --git a/core/embed/rtl/src/sysexit.rs b/core/embed/rtl/src/sysexit.rs
new file mode 100644
index 00000000..1808ccb4
--- /dev/null
+++ b/core/embed/rtl/src/sysexit.rs
@@ -0,0 +1,42 @@
+use crate::ffi;
+use crate::util::FatPtr;
+
+pub fn system_exit() -> ! {
+ // SAFETY: safe
+ unsafe { ffi::system_exit(0) }
+}
+
+pub fn system_exit_error(title: Option<&str>, message: &str, footer: Option<&str>) -> ! {
+ let message_ptr = FatPtr::from(message);
+ let title_ptr = title.map(FatPtr::from).unwrap_or_else(FatPtr::null);
+ let footer_ptr = footer.map(FatPtr::from).unwrap_or_else(FatPtr::null);
+
+ // SAFETY: safe
+ unsafe {
+ ffi::system_exit_error_ex(
+ title_ptr.ptr(),
+ title_ptr.len(),
+ message_ptr.ptr(),
+ message_ptr.len(),
+ footer_ptr.ptr(),
+ footer_ptr.len(),
+ )
+ }
+}
+
+#[inline(never)] // saves few kilobytes of flash
+pub fn system_exit_fatal(message: &str, file: &str, line: u32) -> ! {
+ let message_ptr = FatPtr::from(message);
+ let file_ptr = FatPtr::from(file);
+
+ // SAFETY: safe
+ unsafe {
+ ffi::system_exit_fatal_ex(
+ message_ptr.ptr(),
+ message_ptr.len(),
+ file_ptr.ptr(),
+ file_ptr.len(),
+ line as i32,
+ )
+ }
+}
diff --git a/core/embed/rtl/src/test_setup.c b/core/embed/rtl/src/test_setup.c
deleted file mode 100644
index bfdb8285..00000000
--- a/core/embed/rtl/src/test_setup.c
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * This file is part of the Trezor project, https://trezor.io/
- *
- * Copyright (c) SatoshiLabs
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-// Provide definitions of the system exit functions so that they can be
-// called without linking the sys crate. This is needed when compiling the
-// tests for the crates that don't depend on sys, such as the crypto crate.
-
-#include <stdio.h>
-#include <stdlib.h>
-
-void system_exit_error(const char *title, const char *message,
- const char *footer) {
- printf("Error: %s\n", message);
- if (title != NULL && *title != '\0') {
- printf("Title: %s\n", title);
- }
-
- if (footer != NULL && *footer != '\0') {
- printf("Footer: %s\n", footer);
- }
-
- exit(1);
-}
-
-void system_exit_fatal(const char *message, const char *file, int line) {
- printf("Fatal error: %s", message);
- if (file != NULL && *file != '\0') {
- printf(" at %s:%d", file, line);
- }
- printf("\n");
-
- exit(1);
-}
diff --git a/core/embed/rtl/src/util.rs b/core/embed/rtl/src/util.rs
new file mode 100644
index 00000000..e9872d72
--- /dev/null
+++ b/core/embed/rtl/src/util.rs
@@ -0,0 +1,75 @@
+/// Explicit fat pointer representation
+///
+/// Useful when passing slices into C
+///
+/// # Safety
+///
+/// A FatPtr is a fat representation of a Rust pointer type. It intentionally
+/// does not have an associated lifetime. When created from a slice that later
+/// goes out of scope, it may become invalid. Treat with care.
+#[repr(C)]
+pub struct FatPtr<T> {
+ ptr: *const T,
+ len: usize,
+}
+
+impl<T> FatPtr<T> {
+ /// Create a null fatpointer with zero length
+ pub fn null() -> Self {
+ Self {
+ ptr: core::ptr::null(),
+ len: 0,
+ }
+ }
+
+ /// Convert the fat pointer into a slice
+ ///
+ /// Returns `None` if the fat pointer is null, a slice otherwise.
+ ///
+ /// # Safety
+ ///
+ /// The call reduces to [`core::slice::from_raw_parts`], so all its safety
+ /// properties apply.
+ pub unsafe fn into_slice<'a>(self) -> Option<&'a [T]> {
+ if self.ptr.is_null() {
+ None
+ } else {
+ Some(unsafe { core::slice::from_raw_parts(self.ptr, self.len) })
+ }
+ }
+
+ pub fn is_null(&self) -> bool {
+ self.ptr.is_null()
+ }
+
+ pub fn ptr(&self) -> *const T {
+ self.ptr
+ }
+
+ pub fn len(&self) -> usize {
+ self.len
+ }
+
+ pub fn is_empty(&self) -> bool {
+ self.len == 0
+ }
+}
+
+impl<T> From<&[T]> for FatPtr<T> {
+ fn from(s: &[T]) -> Self {
+ Self {
+ ptr: s.as_ptr(),
+ len: s.len(),
+ }
+ }
+}
+
+// Helper for converting &str to (signed) char*
+impl From<&str> for FatPtr<cty::c_char> {
+ fn from(s: &str) -> Self {
+ Self {
+ ptr: s.as_ptr() as *const cty::c_char,
+ len: s.len(),
+ }
+ }
+}
diff --git a/core/embed/rust/Cargo.toml b/core/embed/rust/Cargo.toml
index 4c0ce910..82000c09 100644
--- a/core/embed/rust/Cargo.toml
+++ b/core/embed/rust/Cargo.toml
@@ -111,6 +111,7 @@ num-derive.workspace = true
num-traits.workspace = true
pareen.workspace = true
qrcodegen.workspace = true
+rtl.workspace = true
spin.workspace = true
static-alloc.workspace = true
trezor-tjpgdec.workspace = true
diff --git a/core/embed/rust/src/lib.rs b/core/embed/rust/src/lib.rs
index 1dd8bf1a..cdfa02f2 100644
--- a/core/embed/rust/src/lib.rs
+++ b/core/embed/rust/src/lib.rs
@@ -56,6 +56,10 @@ pub mod util;
#[cfg(feature = "bootloader")]
mod bootloader;
+// pull in the unwrap! / ensure! / fatal_error! macros
+#[macro_use]
+extern crate rtl;
+
#[cfg(feature = "debug")]
#[cfg(not(test))]
#[panic_handler]
@@ -67,9 +71,9 @@ fn panic_debug(panic_info: &core::panic::PanicInfo) -> ! {
// TODO: find out how to display message from panic_info.message()
let msg = panic_info.message().as_str().unwrap_or("rs");
if let Some(location) = panic_info.location() {
- trezorhal::fatal_error::__fatal_error(msg, location.file(), location.line());
+ rtl::system_exit_fatal(msg, location.file(), location.line());
} else {
- trezorhal::fatal_error::__fatal_error(msg, "", 0);
+ rtl::system_exit_fatal(msg, "", 0);
}
}
diff --git a/core/embed/rust/src/macros.rs b/core/embed/rust/src/macros.rs
index 2544cbf2..5b4ca1b6 100644
--- a/core/embed/rust/src/macros.rs
+++ b/core/embed/rust/src/macros.rs
@@ -1,28 +1,3 @@
-macro_rules! unwrap {
- ($e:expr, $msg:expr) => {{
- use $crate::trezorhal::fatal_error::UnwrapOrFatalError;
- $e.unwrap_or_fatal_error($msg, file!(), line!())
- }};
- ($expr:expr) => {
- unwrap!($expr, "unwrap failed")
- };
-}
-
-#[allow(unused_macros)]
-macro_rules! ensure {
- ($what:expr, $error:expr) => {
- if !($what) {
- $crate::trezorhal::fatal_error::__fatal_error($error, file!(), line!());
- }
- };
-}
-
-macro_rules! fatal_error {
- ($msg:expr) => {{
- $crate::trezorhal::fatal_error::__fatal_error($msg, file!(), line!());
- }};
-}
-
// from https://docs.rs/ufmt/latest/ufmt/
// like `std::format!` it returns a `heapless::String` but uses `uwrite!`
// instead of `write!`
diff --git a/core/embed/rust/src/trezorhal/fatal_error.rs b/core/embed/rust/src/trezorhal/fatal_error.rs
deleted file mode 100644
index fe62d4ab..00000000
--- a/core/embed/rust/src/trezorhal/fatal_error.rs
+++ /dev/null
@@ -1,71 +0,0 @@
-mod ffi {
- extern "C" {
- // system.h
- pub fn system_exit_error_ex(
- title: *const cty::c_char,
- title_len: usize,
- message: *const cty::c_char,
- message_len: usize,
- footer: *const cty::c_char,
- footer_len: usize,
- ) -> !;
- }
-}
-
-pub fn error_shutdown(msg: &str) -> ! {
- unsafe {
- // SAFETY: we pass a valid string to the C function
- // and the function does not return.
- ffi::system_exit_error_ex(
- core::ptr::null(),
- 0,
- msg.as_ptr() as *const cty::c_char,
- msg.len(),
- core::ptr::null(),
- 0,
- );
- }
-}
-
-/// Shows an error message on the screen and shuts down the device.
-/// In debug mode, also prints the error message to the console.
-#[inline(never)] // saves few kilobytes of flash
-pub fn __fatal_error(msg: &str, _file: &str, _line: u32) -> ! {
- #[cfg(feature = "debug")]
- {
- dbg_println!("=== FATAL ERROR");
-
- if _line != 0 {
- dbg_println!("Location: {}:{}", _file, _line);
- }
- if !msg.is_empty() {
- dbg_println!("Message: {}", msg);
- }
-
- dbg_println!("===");
- }
-
- error_shutdown(msg);
-}
-
-pub trait UnwrapOrFatalError<T> {
- fn unwrap_or_fatal_error(self, msg: &str, file: &str, line: u32) -> T;
-}
-
-impl<T> UnwrapOrFatalError<T> for Option<T> {
- fn unwrap_or_fatal_error(self, msg: &str, file: &str, line: u32) -> T {
- match self {
- Some(x) => x,
- None => __fatal_error(msg, file, line),
- }
- }
-}
-
-impl<T, E> UnwrapOrFatalError<T> for Result<T, E> {
- fn unwrap_or_fatal_error(self, msg: &str, file: &str, line: u32) -> T {
- match self {
- Ok(x) => x,
- Err(_) => __fatal_error(msg, file, line),
- }
- }
-}
diff --git a/core/embed/rust/src/trezorhal/mod.rs b/core/embed/rust/src/trezorhal/mod.rs
index b3dc96ae..b2447a09 100644
--- a/core/embed/rust/src/trezorhal/mod.rs
+++ b/core/embed/rust/src/trezorhal/mod.rs
@@ -1,11 +1,8 @@
pub mod bip39;
-#[cfg(feature = "ble")]
-pub mod ble;
-#[macro_use]
-#[allow(unused_macros)]
-pub mod fatal_error;
#[cfg(feature = "ui")]
pub mod bitblt;
+#[cfg(feature = "ble")]
+pub mod ble;
#[cfg(feature = "ui")]
pub mod display;
mod ffi;
diff --git a/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs b/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs
index 144dbb4d..7ce518d6 100644
--- a/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs
+++ b/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs
@@ -587,7 +587,8 @@ impl FirmwareUI for UICaesar {
let (btn_layout, btn_actions) = btns_info_page(is_last);
let mut ops = OpTextLayout::new(theme::TEXT_MONO);
- for item in unwrap!(IterBuf::new().try_iterate(*info_obj)) {
+ let mut iter_buf = IterBuf::new();
+ for item in unwrap!(iter_buf.try_iterate(*info_obj)) {
let [key, value, _is_data]: [Obj; 3] = unwrap!(util::iter_into_array(item));
if !ops.is_empty() {
// Each key-value pair is on its own page
diff --git a/core/embed/sys/task/inc/sys/systask.h b/core/embed/sys/task/inc/sys/systask.h
index 3e838055..cf60819d 100644
--- a/core/embed/sys/task/inc/sys/systask.h
+++ b/core/embed/sys/task/inc/sys/systask.h
@@ -360,7 +360,7 @@ systask_id_t systask_id(const systask_t* task);
* @param task Pointer to the task to terminate, or NULL.
* @param exit_code Exit code for the task.
*/
-void systask_exit(systask_t* task, int exit_code);
+void __attribute__((noreturn)) systask_exit(systask_t* task, int exit_code);
/**
* @brief Terminates the task with an error message
@@ -375,9 +375,9 @@ void systask_exit(systask_t* task, int exit_code);
* @param footer Footer string.
* @param footer_len Length of the footer.
*/
-void systask_exit_error(systask_t* task, const char* title, size_t title_len,
- const char* message, size_t message_len,
- const char* footer, size_t footer_len);
+void __attribute__((noreturn)) systask_exit_error(
+ systask_t* task, const char* title, size_t title_len, const char* message,
+ size_t message_len, const char* footer, size_t footer_len);
/**
* @brief Terminates the task with a fatal error message
@@ -391,9 +391,11 @@ void systask_exit_error(systask_t* task, const char* title, size_t title_len,
* @param file_len Length of the file string.
* @param line Line number.
*/
-void systask_exit_fatal(systask_t* task, const char* message,
- size_t message_len, const char* file, size_t file_len,
- int line);
+void __attribute__((noreturn)) systask_exit_fatal(systask_t* task,
+ const char* message,
+ size_t message_len,
+ const char* file,
+ size_t file_len, int line);
/**
* @brief Prints the post-mortem information about the task to the debug output
diff --git a/core/embed/sys/task/inc/sys/system.h b/core/embed/sys/task/inc/sys/system.h
index ff6f25cc..5541724c 100644
--- a/core/embed/sys/task/inc/sys/system.h
+++ b/core/embed/sys/task/inc/sys/system.h
@@ -19,6 +19,7 @@
#pragma once
+#include <rtl/sysexit.h>
#include <sys/systask.h>
#ifdef KERNEL_MODE
@@ -65,66 +66,6 @@ __attribute__((noreturn)) void system_emergency_rescue(
#endif // KERNEL_MODE
-/**
- * @brief Terminates the current task normally with the given exit code.
- *
- * If the current task is the kernel task, the error handler is called with the
- * postmortem information. If the task is not the kernel task, the task is
- * terminated immediately and the kernel task is scheduled.
- *
- * @param exitcode Exit code returned by the terminating task.
- */
-void system_exit(int exitcode);
-
-/**
- * @brief Terminates the current task with an error message.
- *
- * See the notes for `system_exit` regarding the behavior of the error handler
- *
- * @param title Title of the error.
- * @param message Main error message.
- * @param footer Footer text for the error display.
- */
-void system_exit_error(const char* title, const char* message,
- const char* footer);
-
-/**
- * @brief Like `system_exit_error`, but with explicit lengths for the strings.
- *
- * @param title Title of the error.
- * @param title_len Length of the title.
- * @param message Main error message.
- * @param message_len Length of the message.
- * @param footer Footer text for the error display.
- * @param footer_len Length of the footer.
- */
-void system_exit_error_ex(const char* title, size_t title_len,
- const char* message, size_t message_len,
- const char* footer, size_t footer_len);
-
-/**
- * @brief Terminates the current task with a fatal error message.
- *
- * See the notes for `system_exit` regarding the behavior of the error handler
- *
- * @param message Fatal error message.
- * @param file Source file where the fatal error occurred.
- * @param line Line number in the source file.
- */
-void system_exit_fatal(const char* message, const char* file, int line);
-
-/**
- * @brief Like `system_exit_fatal`, but with explicit lengths for the strings.
- *
- * @param message Fatal error message.
- * @param message_len Length of the message.
- * @param file Source file where the fatal error occurred.
- * @param file_len Length of the file string.
- * @param line Line number in the source file.
- */
-void system_exit_fatal_ex(const char* message, size_t message_len,
- const char* file, size_t file_len, int line);
-
/**
* @brief Returns string representation of the system fault.
*
diff --git a/core/embed/sys/task/stm32/systask.c b/core/embed/sys/task/stm32/systask.c
index 0598181d..44e03577 100644
--- a/core/embed/sys/task/stm32/systask.c
+++ b/core/embed/sys/task/stm32/systask.c
@@ -340,7 +340,7 @@ uint32_t systask_get_r0(systask_t* task) {
return stack[STK_FRAME_R0];
}
-static void systask_kill(systask_t* task) {
+static void __attribute__((noreturn)) systask_kill(systask_t* task) {
systask_scheduler_t* scheduler = &g_systask_scheduler;
task->killed = 1;
@@ -362,6 +362,10 @@ static void systask_kill(systask_t* task) {
// Switch to the kernel task
systask_yield_to(&scheduler->kernel_task);
}
+
+ while (1) {
+ // This point should never be reached
+ }
}
bool systask_is_alive(const systask_t* task) {
diff --git a/core/embed/sys/task/system.c b/core/embed/sys/task/system.c
index bd846f61..be49b550 100644
--- a/core/embed/sys/task/system.c
+++ b/core/embed/sys/task/system.c
@@ -39,19 +39,3 @@ void system_exit_fatal_ex(const char* message, size_t message_len,
}
#endif // KERNEL_MODE
-
-void system_exit_error(const char* title, const char* message,
- const char* footer) {
- size_t title_len = title != NULL ? strlen(title) : 0;
- size_t message_len = message != NULL ? strlen(message) : 0;
- size_t footer_len = footer != NULL ? strlen(footer) : 0;
-
- system_exit_error_ex(title, title_len, message, message_len, footer,
- footer_len);
-}
-
-void system_exit_fatal(const char* message, const char* file, int line) {
- size_t message_len = message != NULL ? strlen(message) : 0;
- size_t file_len = file != NULL ? strlen(file) : 0;
- system_exit_fatal_ex(message, message_len, file, file_len, line);
-}
diff --git a/core/embed/sys/task/unix/systask.c b/core/embed/sys/task/unix/systask.c
index 594cd740..0a1afe3b 100644
--- a/core/embed/sys/task/unix/systask.c
+++ b/core/embed/sys/task/unix/systask.c
@@ -224,7 +224,7 @@ bool systask_push_call(systask_t* task, void* fn, uintptr_t arg1,
return true;
}
-static void systask_kill(systask_t* task) {
+static void __attribute__((noreturn)) systask_kill(systask_t* task) {
systask_scheduler_t* scheduler = &g_systask_scheduler;
systask_print_pminfo(task);
@@ -248,6 +248,10 @@ static void systask_kill(systask_t* task) {
// Switch to the kernel task
systask_yield_to(&scheduler->kernel_task);
}
+
+ while (1) {
+ // This point should never be reached
+ }
}
bool systask_is_alive(const systask_t* task) {
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.