What changed, and why it matters
This commit is a straightforward internal refactoring that adds a new Rust logging bridge. It does not change any security-critical behavior: logging is already disabled in release builds, and the new code is only compiled into debug builds. There is no indication this fixes or introduces a security vulnerability.
No security action required. Review as normal code-quality/logging refactor.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch introduces a log crate adapter (util/logger.rs) that forwards Rust log::error!, log::warn!, etc. macros to the existing Trezor syslog C backend. It exposes a new MicroPython trezorlog.init(level) function and calls it from trezor/log.py during debug builds. The log dependency is configured with release_max_level_off, so the logging machinery is compiled out of release firmware. The change is gated by the dbg_console feature and __debug__ Python flag.
Changed components
core/embed/rust/src/micropython/logging.rscore/embed/rust/src/util/logger.rscore/embed/rust/src/util/mod.rscore/src/trezor/log.pycore/embed/rust/Cargo.tomlcore/embed/rust/Cargo.lockcore/mocks/generated/trezorlog.pyiInspect captured patch +118 / −1
diff --git a/core/embed/rust/Cargo.lock b/core/embed/rust/Cargo.lock
index 2403afad..3b1bfac2 100644
--- a/core/embed/rust/Cargo.lock
+++ b/core/embed/rust/Cargo.lock
@@ -167,6 +167,12 @@ version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
+[[package]]
+name = "log"
+version = "0.4.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
+
[[package]]
name = "memchr"
version = "2.4.1"
@@ -355,6 +361,7 @@ dependencies = [
"glob",
"heapless",
"hex",
+ "log",
"minicbor",
"num-derive",
"num-traits",
diff --git a/core/embed/rust/Cargo.toml b/core/embed/rust/Cargo.toml
index 075c4f97..5b61ef49 100644
--- a/core/embed/rust/Cargo.toml
+++ b/core/embed/rust/Cargo.toml
@@ -133,6 +133,11 @@ version = "0.9.2"
features = ["ufmt"]
default-features = false
+[dependencies.log]
+version = "0.4.29"
+# Disable logging for release profile.
+features = ["max_level_trace", "release_max_level_off"]
+
[dependencies.num-traits]
version = "0.2.19"
default-features = false
diff --git a/core/embed/rust/src/micropython/logging.rs b/core/embed/rust/src/micropython/logging.rs
index 8d767632..46ca01bc 100644
--- a/core/embed/rust/src/micropython/logging.rs
+++ b/core/embed/rust/src/micropython/logging.rs
@@ -5,6 +5,7 @@ use crate::{
error::Error,
micropython::{buffer::StrBuffer, util},
trezorhal::syslog::{syslog_start_record, syslog_write_chunk, LogLevel},
+ util::logger::init_rust_logging,
};
#[cfg(feature = "dbg_console")]
@@ -75,6 +76,20 @@ extern "C" fn py_error(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj
Obj::const_none()
}
+extern "C" fn py_init(level: Obj) -> Obj {
+ #[cfg(feature = "dbg_console")]
+ {
+ let block = || {
+ init_rust_logging(level.try_into()?);
+ Ok(())
+ };
+ unsafe {
+ util::try_or_raise(block);
+ }
+ }
+ Obj::const_none()
+}
+
#[no_mangle]
#[rustfmt::skip]
pub static mp_module_trezorlog: Module = obj_module! {
@@ -97,4 +112,10 @@ pub static mp_module_trezorlog: Module = obj_module! {
/// def error(name: str, msg: str, *args: Any, *, iface: WireInterface | None = None) -> None:
/// ...
Qstr::MP_QSTR_error => obj_fn_kw!(2, py_error).as_obj(),
+
+ /// def init(level: int) -> None:
+ /// """
+ /// Initialize Rust logging connector.
+ /// """
+ Qstr::MP_QSTR_init => obj_fn_1!(py_init).as_obj(),
};
diff --git a/core/embed/rust/src/util/logger.rs b/core/embed/rust/src/util/logger.rs
new file mode 100644
index 00000000..6972fc72
--- /dev/null
+++ b/core/embed/rust/src/util/logger.rs
@@ -0,0 +1,74 @@
+//! Connects the `log::error!`, `log::warn!`, ... macros from the `log` crate to
+//! our C logging backend.
+
+use heapless::Vec;
+use log::{set_logger, set_max_level, Level, LevelFilter, Log, Metadata, Record};
+
+use core::{
+ fmt::Write,
+ sync::atomic::{AtomicBool, Ordering},
+};
+
+use crate::trezorhal::syslog::{syslog_start_record, syslog_write_chunk, LogLevel};
+
+const MAX_MESSAGE_LEN: usize = 128;
+
+static INITIALIZED: AtomicBool = AtomicBool::new(false);
+
+struct SysLogger;
+
+fn sys_level(level: Level) -> LogLevel {
+ match level {
+ Level::Error => LogLevel::Error,
+ Level::Warn => LogLevel::Warn,
+ Level::Info => LogLevel::Info,
+ Level::Debug | Level::Trace => LogLevel::Debug,
+ }
+}
+
+impl Log for SysLogger {
+ fn enabled(&self, _metadata: &Metadata) -> bool {
+ // The `log` crate already compares the level, `syslog_start_record` takes care
+ // of filtering by module. Implementing it here would only make sense if we used
+ // `log_enabled!` heavily.
+ true
+ }
+
+ fn log(&self, record: &Record) {
+ if !self.enabled(record.metadata()) {
+ return;
+ }
+
+ let should_log = syslog_start_record(record.target(), sys_level(record.level()));
+ if !should_log {
+ return;
+ }
+
+ let mut msg = Vec::<u8, MAX_MESSAGE_LEN>::new();
+ // Might still get partial message on error.
+ let _ = msg.write_fmt(*record.args());
+
+ // SAFETY: passed to C which doesn't care about UTF-8
+ let text = unsafe { str::from_utf8_unchecked(&msg) };
+ syslog_write_chunk(text, true);
+ }
+
+ fn flush(&self) {}
+}
+
+fn to_filter(val: u8) -> LevelFilter {
+ match val {
+ 0 => LevelFilter::Trace, // corresponds to debug in micropython
+ 1 => LevelFilter::Info,
+ 2 => LevelFilter::Warn,
+ 3 => LevelFilter::Error,
+ _ => LevelFilter::Off,
+ }
+}
+
+pub fn init_rust_logging(level: u8) {
+ if !INITIALIZED.swap(true, Ordering::Relaxed) {
+ let _ = set_logger(&SysLogger);
+ set_max_level(to_filter(level));
+ }
+}
diff --git a/core/embed/rust/src/util/mod.rs b/core/embed/rust/src/util/mod.rs
index f05f097c..b22740a6 100644
--- a/core/embed/rust/src/util/mod.rs
+++ b/core/embed/rust/src/util/mod.rs
@@ -1,5 +1,7 @@
#[cfg(feature = "micropython")]
pub mod interpolate;
+#[cfg(feature = "dbg_console")]
+pub mod logger;
/// Constructs a string from a C string.
///
diff --git a/core/mocks/generated/trezorlog.pyi b/core/mocks/generated/trezorlog.pyi
index 8acf3629..166a6b47 100644
--- a/core/mocks/generated/trezorlog.pyi
+++ b/core/mocks/generated/trezorlog.pyi
@@ -20,3 +20,10 @@ def warning(name: str, msg: str, *args: Any, *, iface: WireInterface | None = No
# rust/src/micropython/logging.rs
def error(name: str, msg: str, *args: Any, *, iface: WireInterface | None = None) -> None:
...
+
+
+# rust/src/micropython/logging.rs
+def init(level: int) -> None:
+ """
+ Initialize Rust logging connector.
+ """
diff --git a/core/src/trezor/log.py b/core/src/trezor/log.py
index 9d33cc4e..308fc311 100644
--- a/core/src/trezor/log.py
+++ b/core/src/trezor/log.py
@@ -11,11 +11,12 @@ def _no_op(name: str, msg: str, *args: Any, iface: WireInterface | None = None)
if __debug__:
- from trezorlog import debug, error, info, warning # noqa: F401
+ from trezorlog import debug, error, info, init, warning # noqa: F401
_levels = [debug, info, warning, error]
_min_level = 0 # can be used for manually disabling low-priority logging levels
debug, info, warning, error = [_no_op] * _min_level + _levels[_min_level:]
+ init(_min_level) # initialize rust logging connector
else:
# logging is disabled in non-debug builds
debug = warning = info = error = _no_op
Why this scored 15/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.