fix(core): centralize rust panic handler in sys crate
What changed, and why it matters
This commit is a code cleanup: it moves the Rust panic handler from several individual firmware components into a single shared system crate. A panic handler is the code that runs when something goes seriously wrong inside the device software. The change does not add new functionality or fix a security bug; it just makes the panic handling code live in one place instead of many. The release builds of Trezor firmware do not even use this handler because they are configured to abort immediately on panic.
No security action required. Treat as routine refactoring. Reviewers may verify that the centralized panic handler is still correctly gated out of release builds and that debug builds still report useful diagnostics.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch removes local #[panic_handler] implementations from boardloader, bootloader_ci, secmon, and core/embed/rust, and adds a centralized handler in core/embed/sys/src/panic.rs. The new handler is conditionally compiled only when panic strategy is not ‘unwind’ (i.e., not host unit tests). It reports file/line information via rtl::system_exit_fatal. The commit message and comments note that release firmware uses panic = ‘immediate-abort’, so this handler is compiled out and unused in release builds. There is no evidence of a vulnerability being fixed.
Changed components
core/embed/projects/boardloader/src/main.rscore/embed/projects/bootloader_ci/src/main.rscore/embed/projects/secmon/src/main.rscore/embed/rust/src/lib.rscore/embed/sys/src/lib.rscore/embed/sys/src/panic.rsInspect captured patch +28 / −46
### core/embed/projects/boardloader/src/main.rs
@@ -1,7 +1,5 @@
#![no_std]
#![no_main]
-#[panic_handler]
-fn panic(_info: &core::panic::PanicInfo) -> ! {
- loop {}
-}
+// force pull in Rust generated symbols (incl. the panic handler)
+use sys as _;
### core/embed/projects/bootloader_ci/src/main.rs
@@ -1,7 +1,5 @@
#![no_std]
#![no_main]
-#[panic_handler]
-fn panic(_info: &core::panic::PanicInfo) -> ! {
- loop {}
-}
+// force pull in Rust generated symbols (incl. the panic handler)
+use sys as _;
### core/embed/projects/secmon/src/main.rs
@@ -1,7 +1,5 @@
#![no_std]
#![no_main]
-#[panic_handler]
-fn panic(_info: &core::panic::PanicInfo) -> ! {
- loop {}
-}
+// force pull in Rust generated symbols (incl. the panic handler)
+use sys as _;
### core/embed/rust/src/lib.rs
@@ -59,40 +59,6 @@ mod bootloader;
#[macro_use]
extern crate rtl;
-#[cfg(feature = "debug")]
-#[cfg(not(test))]
-#[panic_handler]
-/// More detailed panic handling. The difference against
-/// default `panic` below is that this "debug" version
-/// takes around 10 kB more space in the flash region.
-fn panic_debug(panic_info: &core::panic::PanicInfo) -> ! {
- // Filling at least the file and line information, if available.
- // 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() {
- rtl::system_exit_fatal(msg, location.file(), location.line());
- } else {
- rtl::system_exit_fatal(msg, "", 0);
- }
-}
-
-#[cfg(not(feature = "debug"))]
-#[cfg(not(test))]
-#[cfg(any(not(feature = "test"), feature = "clippy"))]
-#[panic_handler]
-/// Default panic handling. Not showing any details - thus saving flash space.
-fn panic(_info: &core::panic::PanicInfo) -> ! {
- // TODO: as of Rust 1.63 / nightly 2022-08, ignoring the `_info` parameter does
- // not help with saving flash space -- the `fmt` machinery still gets
- // compiled in. We can avoid that by using unstable Cargo arguments:
- // -Zbuild-std=core -Zbuild-std-features=panic_immediate_abort
- // Doing that will compile every panic!() to a single udf instruction which
- // raises a Hard Fault on hardware.
- //
- // Otherwise, use `unwrap!` macro from trezorhal.
- fatal_error!("rs");
-}
-
#[cfg(not(target_arch = "arm"))]
#[cfg(not(test))]
#[cfg(any(not(feature = "test"), feature = "clippy"))]
### core/embed/sys/src/lib.rs
@@ -3,6 +3,13 @@
mod ffi;
pub mod irq;
+
+// Compiled out for host-side unit tests, where std provides the handler.
+// Cargo builds test targets and their dependencies with `panic = "unwind"`;
+// all firmware and emulator profiles use `abort` or `immediate-abort`.
+#[cfg(not(panic = "unwind"))]
+mod panic;
+
#[cfg(feature = "dbg_console")]
pub mod syslog;
### core/embed/sys/src/panic.rs
@@ -0,0 +1,15 @@
+/// Panic handler shared by all binaries that link `sys`.
+///
+/// Only debug builds ever reach it - the release profile uses
+/// `panic = "immediate-abort"`, which compiles every panic to an abort
+/// instruction without referencing the handler, so it gets stripped there.
+#[panic_handler]
+fn panic(panic_info: &core::panic::PanicInfo) -> ! {
+ // Filling at least the file and line information, if available.
+ let msg = panic_info.message().as_str().unwrap_or("rs");
+ if let Some(location) = panic_info.location() {
+ rtl::system_exit_fatal(msg, location.file(), location.line());
+ } else {
+ rtl::system_exit_fatal(msg, "", 0);
+ }
+}Why this scored 12/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.