refactor(core/rust): move micropython's Error to micropython module
What changed, and why it matters
This is a routine internal code reorganization in the Trezor firmware's Rust code. It moves the existing `Error` type from a top-level module into the `micropython` module and updates import paths accordingly. There is no change to user-facing behavior, no fix for a vulnerability, and no new security-sensitive logic.
No security action required. Treat as normal refactoring. Reviewers may optionally verify that the re-export in `micropython/mod.rs` does not accidentally duplicate exports (the diff shows `pub use error::Error;` and `pub use obj::Obj;` listed twice).
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit refactors the Error enum from crate::error into crate::micropython::error and re-exports it as crate::micropython::Error. It updates all call sites to use the new path, removes the old error.rs file, and adjusts some helper functions (e.g., missing_required_field, invalid_value) to be const and use to_obj() instead of Into. The AttributeError variant now takes Obj instead of Qstr. These are structural changes in preparation for splitting micropython into its own crate and reworking error handling.
Changed components
core/embed/rust/src/error.rscore/embed/rust/src/micropython/error.rscore/embed/rust/src/micropython/mod.rscore/embed/rust/src/protobuf/error.rscore/embed/rust/src/definitions/obj.rscore/embed/rust/src/micropython/obj.rsInspect captured patch +153 / −187
### core/embed/rust/src/coverage/mod.rs
@@ -2,7 +2,7 @@
use heapless::index_map::{Entry, FnvIndexMap};
use spin::RwLock;
-use crate::error::Error;
+use crate::micropython::error::Error;
use crate::micropython::list::List;
use crate::micropython::macros::{obj_fn_0, obj_fn_2, obj_module};
use crate::micropython::module::Module;
### core/embed/rust/src/definitions/blob.rs
@@ -2,7 +2,7 @@ use crypto::merkle::merkle_root;
use crypto::{cosi, ed25519, sha256};
use super::{constants, generated};
-use crate::error::Error;
+use crate::micropython::Error;
use crate::io::InputStream;
const INVALID_DEFINITION: Error = Error::ExternalDataError(c"Invalid definition");
### core/embed/rust/src/definitions/obj.rs
@@ -1,16 +1,29 @@
-use super::blob;
-use crate::error::Error;
-use crate::io::InputStream;
+use crypto::{cosi, ed25519};
+
+use super::constants;
use crate::micropython::buffer::get_buffer;
use crate::micropython::gc::Gc;
use crate::micropython::macros::{obj_fn_var, obj_module};
use crate::micropython::map::Map;
use crate::micropython::module::Module;
-use crate::micropython::obj::Obj;
use crate::micropython::qstr::Qstr;
-use crate::micropython::util;
-use crate::protobuf::decode::Decoder;
-use crate::protobuf::obj::MsgDefObj;
+use crate::micropython::{util, Error, Obj};
+
+fn verify_with_keys(
+ threshold: u8,
+ digest: &[u8],
+ sig: &cosi::Signature,
+ public_keys: &[ed25519::PublicKey; 3],
+) -> Result<(), Error> {
+ cosi::verify(threshold, digest, public_keys, sig)
+ .map_err(|_| Error::ValueError(c"Signature verification failed"))
+}
+
+fn threshold_for_version(version: u8) -> Result<u8, Error> {
+ let version = constants::DefsVersion::from_byte(version)
+ .ok_or(Error::ValueError(c"Unsupported definition format version"))?;
+ Ok(version.threshold())
+}
extern "C" fn decode(n_args: usize, args: *const Obj) -> Obj {
let block = |args: &[Obj], _kwargs: &Map| {
### core/embed/rust/src/error.rs
@@ -1,135 +0,0 @@
-use core::convert::Infallible;
-use core::ffi::CStr;
-use core::num::TryFromIntError;
-
-#[cfg(feature = "micropython")]
-use {
- crate::micropython::{
- exception::{self, new_exception, new_exception_arg_from, new_exception_args},
- obj::Obj,
- qstr::Qstr,
- },
- core::convert::TryInto,
-};
-
-#[cfg(feature = "thp")]
-use crate::thp::micropython::ThpError;
-
-#[allow(clippy::enum_variant_names)] // We mimic the Python exception classnames here.
-#[derive(Clone, Copy, Debug)]
-pub enum Error {
- TypeError,
- OutOfRange,
- MissingKwargs,
- AllocationFailed,
- EOFError,
- IndexError,
- #[cfg(feature = "micropython")]
- CaughtException(Obj),
- #[cfg(feature = "micropython")]
- KeyError(Obj),
- #[cfg(feature = "micropython")]
- AttributeError(Qstr),
- ValueError(&'static CStr),
- #[cfg(feature = "micropython")]
- ValueErrorParam(&'static CStr, Obj),
- RuntimeError(&'static CStr),
- NotImplementedError,
- #[cfg(feature = "thp")]
- ThpError(&'static CStr),
- ExternalDataError(&'static CStr),
-}
-
-#[allow(unused_macros)]
-macro_rules! value_error {
- ($msg:expr) => {
- $crate::error::Error::ValueError($msg)
- };
-}
-
-#[allow(unused_imports)]
-pub(crate) use value_error;
-
-#[cfg(feature = "micropython")]
-impl Error {
- /// Create an exception instance matching the error code. The result of this
- /// call should only be used to immediately raise the exception, because the
- /// object is not guaranteed to remain intact. MicroPython might reuse the
- /// same space for creating a different exception.
- pub unsafe fn into_obj(self) -> Obj {
- unsafe {
- // SAFETY: First argument is a reference to a valid exception type.
- // EXCEPTION: Sensibly, `new_exception_*` does not raise.
- match self {
- Error::TypeError => new_exception(exception::TypeError),
- Error::OutOfRange => new_exception(exception::OverflowError),
- Error::MissingKwargs => new_exception(exception::TypeError),
- Error::AllocationFailed => new_exception(exception::MemoryError),
- Error::IndexError => new_exception(exception::IndexError),
- Error::CaughtException(obj) => obj,
- Error::KeyError(key) => new_exception_arg_from(exception::KeyError, key),
- Error::ValueError(msg) => new_exception_arg_from(exception::ValueError, msg),
- Error::ValueErrorParam(msg, param) => match msg.try_into() {
- Ok(msg) => new_exception_args(exception::ValueError, &[msg, param]),
- _ => new_exception(exception::ValueError),
- },
- Error::AttributeError(attr) => {
- new_exception_arg_from(exception::AttributeError, attr)
- }
- Error::EOFError => new_exception(exception::EOFError),
- Error::RuntimeError(msg) => new_exception_arg_from(exception::RuntimeError, msg),
- Error::NotImplementedError => new_exception(exception::NotImplementedError),
- #[cfg(feature = "thp")]
- Error::ThpError(msg) => new_exception_arg_from(&ThpError, msg),
- Error::ExternalDataError(msg) => {
- new_exception_arg_from(&exception::ExternalDataError, msg)
- }
- }
- }
- }
-}
-
-// Implements a conversion from `core::convert::Infallible` to `Error` to so
-// that code generic over `TryFrom` can work with values covered by the blanket
-// impl for `Into`: `https://doc.rust-lang.org/std/convert/enum.Infallible.html`
-impl From<Infallible> for Error {
- fn from(_: Infallible) -> Self {
- unreachable!()
- }
-}
-
-impl From<TryFromIntError> for Error {
- fn from(_: TryFromIntError) -> Self {
- Self::OutOfRange
- }
-}
-
-#[cfg(feature = "thp")]
-impl From<trezor_thp::Error> for Error {
- fn from(error: trezor_thp::Error) -> Self {
- match error {
- trezor_thp::Error::UnexpectedInput => Error::ThpError(c"Unexpected input"),
- trezor_thp::Error::NotReady => Error::ThpError(c"Not ready"),
- trezor_thp::Error::MalformedData => Error::ThpError(c"Malformed data"),
- trezor_thp::Error::InvalidChecksum => Error::ThpError(c"Invalid checksum"),
- trezor_thp::Error::InsufficientBuffer => Error::ThpError(c"Insufficient buffer"),
- trezor_thp::Error::CryptoError => Error::ThpError(c"Crypto error"),
- }
- }
-}
-
-#[cfg(feature = "crypto")]
-impl From<crypto::Error> for crate::error::Error {
- fn from(e: crypto::Error) -> Self {
- match e {
- crypto::Error::SignatureVerificationFailed => {
- value_error!(c"Signature verification failed")
- }
- crypto::Error::InvalidEncoding => value_error!(c"Invalid key or signature encoding"),
- crypto::Error::InvalidParams => value_error!(c"Invalid cryptographic parameters"),
- crypto::Error::InvalidContext => value_error!(c"Invalid cryptographic context"),
- crypto::Error::AuthenticationFailed => value_error!(c"Authentication failed"),
- crypto::Error::InvalidSigmask => value_error!(c"Invalid sigmask"),
- }
- }
-}
### core/embed/rust/src/lib.rs
@@ -32,7 +32,6 @@ mod align;
mod coverage;
#[cfg(feature = "universal_fw")]
mod definitions;
-mod error;
mod io;
mod maybe_trace;
#[cfg(feature = "micropython")]
### core/embed/rust/src/micropython/buffer.rs
@@ -2,9 +2,9 @@ use core::convert::TryFrom;
use core::ops::Deref;
use core::{ptr, slice, str};
+use super::error::Error;
use super::ffi;
-use crate::error::Error;
-use crate::micropython::obj::Obj;
+use super::obj::Obj;
use crate::strutil::hexlify;
/// Represents an immutable UTF-8 string managed by MicroPython GC.
### core/embed/rust/src/micropython/dict.rs
@@ -1,11 +1,11 @@
use core::convert::TryFrom;
+use super::error::Error;
use super::ffi;
use super::gc::Gc;
use super::map::Map;
use super::obj::Obj;
use super::runtime::catch_exception;
-use crate::error::Error;
/// Insides of the MicroPython `dict` object.
pub type Dict = ffi::mp_obj_dict_t;
### core/embed/rust/src/micropython/error.rs
@@ -0,0 +1,93 @@
+use core::{
+ convert::{Infallible, TryInto},
+ ffi::CStr,
+ num::TryFromIntError,
+};
+
+use crate::micropython::{ffi, Obj};
+
+#[allow(clippy::enum_variant_names)] // We mimic the Python exception classnames here.
+#[derive(Clone, Copy, Debug)]
+pub enum Error {
+ TypeError,
+ OutOfRange,
+ MissingKwargs,
+ AllocationFailed,
+ EOFError,
+ IndexError,
+ CaughtException(Obj),
+ KeyError(Obj),
+ AttributeError(Obj),
+ ValueError(&'static CStr),
+ ValueErrorParam(&'static CStr, Obj),
+ RuntimeError(&'static CStr),
+ NotImplementedError,
+}
+
+impl Error {
+ /// Create an exception instance matching the error code. The result of this
+ /// call should only be used to immediately raise the exception, because the
+ /// object is not guaranteed to remain intact. MicroPython might reuse the
+ /// same space for creating a different exception.
+ pub unsafe fn into_obj(self) -> Obj {
+ unsafe {
+ // SAFETY: First argument is a reference to a valid exception type.
+ // EXCEPTION: Sensibly, `new_exception_*` does not raise.
+ match self {
+ Error::TypeError => ffi::mp_obj_new_exception(&ffi::mp_type_TypeError),
+ Error::OutOfRange => ffi::mp_obj_new_exception(&ffi::mp_type_OverflowError),
+ Error::MissingKwargs => ffi::mp_obj_new_exception(&ffi::mp_type_TypeError),
+ Error::AllocationFailed => ffi::mp_obj_new_exception(&ffi::mp_type_MemoryError),
+ Error::IndexError => ffi::mp_obj_new_exception(&ffi::mp_type_IndexError),
+ Error::CaughtException(obj) => obj,
+ Error::KeyError(key) => {
+ ffi::mp_obj_new_exception_args(&ffi::mp_type_KeyError, 1, &key)
+ }
+ Error::ValueError(msg) => {
+ if let Ok(msg) = msg.try_into() {
+ ffi::mp_obj_new_exception_args(&ffi::mp_type_ValueError, 1, &msg)
+ } else {
+ ffi::mp_obj_new_exception(&ffi::mp_type_ValueError)
+ }
+ }
+ Error::ValueErrorParam(msg, param) => {
+ if let Ok(msg) = msg.try_into() {
+ let args = [msg, param];
+ ffi::mp_obj_new_exception_args(&ffi::mp_type_ValueError, 2, args.as_ptr())
+ } else {
+ ffi::mp_obj_new_exception(&ffi::mp_type_ValueError)
+ }
+ }
+ Error::AttributeError(attr) => {
+ ffi::mp_obj_new_exception_args(&ffi::mp_type_AttributeError, 1, &attr)
+ }
+ Error::EOFError => ffi::mp_obj_new_exception(&ffi::mp_type_EOFError),
+ Error::RuntimeError(msg) => {
+ if let Ok(msg) = msg.try_into() {
+ ffi::mp_obj_new_exception_args(&ffi::mp_type_RuntimeError, 1, &msg)
+ } else {
+ ffi::mp_obj_new_exception(&ffi::mp_type_RuntimeError)
+ }
+ }
+ Error::NotImplementedError => {
+ ffi::mp_obj_new_exception(&ffi::mp_type_NotImplementedError)
+ }
+ }
+ }
+ }
+}
+
+// Implements a conversion from `core::convert::Infallible` to `Error` to so
+// that code generic over `TryFrom` can work with values covered by the blanket
+// impl for `Into`: `https://doc.rust-lang.org/std/convert/enum.Infallible.html`
+impl From<Infallible> for Error {
+ fn from(_: Infallible) -> Self {
+ unreachable!()
+ }
+}
+
+impl From<TryFromIntError> for Error {
+ fn from(_: TryFromIntError) -> Self {
+ Self::OutOfRange
+ }
+}
### core/embed/rust/src/micropython/gc.rs
@@ -2,8 +2,8 @@ use core::alloc::Layout;
use core::ops::{Deref, DerefMut};
use core::ptr::{self, NonNull};
+use super::error::Error;
use super::ffi;
-use crate::error::Error;
/// A pointer type for values on the garbage-collected heap.
pub struct Gc<T: ?Sized>(NonNull<T>);
### core/embed/rust/src/micropython/iter.rs
@@ -1,9 +1,9 @@
use core::ptr;
+use super::error::Error;
use super::ffi;
+use super::obj::Obj;
use super::runtime::catch_exception;
-use crate::error::Error;
-use crate::micropython::obj::Obj;
pub struct IterBuf {
iter_buf: ffi::mp_obj_iter_buf_t,
### core/embed/rust/src/micropython/list.rs
@@ -1,11 +1,11 @@
use core::convert::TryFrom;
use core::ptr;
+use super::error::Error;
use super::ffi;
use super::gc::{Gc, GcBox};
use super::obj::Obj;
use super::runtime::catch_exception;
-use crate::error::Error;
pub type List = ffi::mp_obj_list_t;
### core/embed/rust/src/micropython/logging.rs
@@ -1,12 +1,10 @@
use sys::syslog::{log, LogLevel};
-use crate::error::Error;
use crate::micropython::buffer::StrBuffer;
use crate::micropython::map::Map;
use crate::micropython::module::Module;
-use crate::micropython::obj::Obj;
use crate::micropython::qstr::Qstr;
-use crate::micropython::util;
+use crate::micropython::{util, Error, Obj};
use crate::util::logger::init_rust_logging;
fn _log(level: LogLevel, args: &[Obj], kwargs: &Map) -> Result<Obj, Error> {
### core/embed/rust/src/micropython/map.rs
@@ -3,11 +3,11 @@ use core::mem::MaybeUninit;
use core::ops::Deref;
use core::{ptr, slice};
+use super::error::Error;
use super::ffi;
+use super::obj::Obj;
+use super::qstr::Qstr;
use super::runtime::catch_exception;
-use crate::error::Error;
-use crate::micropython::obj::Obj;
-use crate::micropython::qstr::Qstr;
pub type Map = ffi::mp_map_t;
pub type MapElem = ffi::mp_map_elem_t;
### core/embed/rust/src/micropython/mod.rs
@@ -5,6 +5,7 @@ pub mod macros;
pub mod buffer;
pub mod dict;
pub mod exception;
+pub mod error;
pub mod ffi;
pub mod func;
pub mod gc;
@@ -20,6 +21,12 @@ pub mod simple_type;
pub mod typ;
pub mod util;
+pub use error::Error;
+pub use obj::Obj;
+
+pub use error::Error;
+pub use obj::Obj;
+
#[cfg(feature = "dbg_console")]
pub mod logging;
### core/embed/rust/src/micropython/obj.rs
@@ -1,9 +1,9 @@
use core::convert::{TryFrom, TryInto};
use core::ffi::CStr;
+use super::error::Error;
use super::ffi;
use super::runtime::catch_exception;
-use crate::error::Error;
pub type Obj = ffi::mp_obj_t;
pub type ObjBase = ffi::mp_obj_base_t;
### core/embed/rust/src/micropython/qstr.rs
@@ -6,9 +6,9 @@ use core::convert::TryFrom;
use core::slice;
use core::str::from_utf8;
+use super::error::Error;
use super::ffi;
use super::obj::Obj;
-use crate::error::Error;
impl Qstr {
pub const fn to_obj(self) -> Obj {
### core/embed/rust/src/micropython/runtime.rs
@@ -1,7 +1,7 @@
use core::mem::MaybeUninit;
+use super::error::Error;
use super::ffi;
-use crate::error::Error;
/// Raise a micropython exception via NLR jump.
/// Jumps directly out of the context without running any destructors,
### core/embed/rust/src/micropython/util.rs
@@ -2,13 +2,13 @@ use core::slice;
use heapless::Vec;
+use super::error::Error;
use super::ffi;
use super::iter::IterBuf;
use super::map::{Map, MapElem};
use super::obj::Obj;
use super::qstr::Qstr;
use super::runtime::{catch_exception, raise_exception};
-use crate::error::{value_error, Error};
/// Perform a call and convert errors into a raised MicroPython exception.
/// Should only called when returning from Rust to C. See `raise_exception` for
@@ -131,7 +131,7 @@ where
let vec: Vec<T, N> = iter_into_vec(iterable)?;
// Returns error if array.len() != N
vec.into_array()
- .map_err(|_| value_error!(c"Invalid iterable length"))
+ .map_err(|_| Error::ValueError(c"Invalid iterable length"))
}
pub fn iter_into_vec<T, E, const N: usize>(iterable: Obj) -> Result<Vec<T, N>, Error>
@@ -142,7 +142,7 @@ where
let mut vec = Vec::<T, N>::new();
for item in IterBuf::new().try_iterate(iterable)? {
vec.push(item.try_into()?)
- .map_err(|_| value_error!(c"Invalid iterable length"))?;
+ .map_err(|_| Error::ValueError(c"Invalid iterable length"))?;
}
Ok(vec)
}
### core/embed/rust/src/protobuf/decode.rs
@@ -4,14 +4,12 @@ use core::str;
use super::defs::{self, FieldDef, FieldType, MsgDef};
use super::obj::{MsgDefObj, MsgObj};
use super::{error, zigzag};
-use crate::error::Error;
use crate::io::InputStream;
use crate::micropython::gc::Gc;
use crate::micropython::list::List;
use crate::micropython::map::Map;
-use crate::micropython::obj::Obj;
use crate::micropython::qstr::Qstr;
-use crate::micropython::{buffer, util};
+use crate::micropython::{buffer, util, Error, Obj};
const MAX_NESTING: u8 = 16;
### core/embed/rust/src/protobuf/encode.rs
@@ -2,14 +2,12 @@ use core::convert::{TryFrom, TryInto};
use super::defs::{FieldDef, FieldType, MsgDef};
use super::obj::MsgObj;
-use super::{error, zigzag};
-use crate::error::Error;
+use super::zigzag;
use crate::micropython::gc::Gc;
use crate::micropython::iter::IterBuf;
use crate::micropython::list::List;
-use crate::micropython::obj::Obj;
use crate::micropython::qstr::Qstr;
-use crate::micropython::{buffer, util};
+use crate::micropython::{buffer, util, Error, Obj};
pub extern "C" fn protobuf_len(obj: Obj) -> Obj {
let block = || {
### core/embed/rust/src/protobuf/error.rs
@@ -1,22 +1,18 @@
-use crate::error::{value_error, Error};
+use crate::micropython::error::Error;
use crate::micropython::qstr::Qstr;
pub const fn experimental_not_enabled() -> Error {
- value_error!(c"Experimental features are disabled.")
+ Error::ValueError(c"Experimental features are disabled.")
}
pub const fn unknown_field_type() -> Error {
- value_error!(c"Unknown field type.")
+ Error::ValueError(c"Unknown field type.")
}
-pub fn missing_required_field(field: Qstr) -> Error {
- Error::ValueErrorParam(c"Missing required field.", field.into())
+pub const fn missing_required_field(field: Qstr) -> Error {
+ Error::ValueErrorParam(c"Missing required field.", field.to_obj())
}
-pub fn invalid_value(field: Qstr) -> Error {
- Error::ValueErrorParam(c"Invalid value for field.", field.into())
-}
-
-pub const fn end_of_buffer() -> Error {
- value_error!(c"End of buffer.")
+pub const fn invalid_value(field: Qstr) -> Error {
+ Error::ValueErrorParam(c"Invalid value for field.", field.to_obj())
}
### core/embed/rust/src/protobuf/obj.rs
@@ -3,7 +3,6 @@ use core::convert::TryFrom;
use super::decode::{protobuf_decode, Decoder};
use super::defs::{find_name_by_msg_offset, get_msg, MsgDef};
use super::encode::{protobuf_encode, protobuf_len};
-use crate::error::Error;
use crate::micropython::dict::Dict;
use crate::micropython::gc::Gc;
use crate::micropython::macros::{obj_fn_1, obj_fn_2, obj_fn_3, obj_module, obj_type};
@@ -12,7 +11,7 @@ use crate::micropython::module::Module;
use crate::micropython::obj::{Obj, ObjBase};
use crate::micropython::qstr::Qstr;
use crate::micropython::typ::{FullType, Type};
-use crate::micropython::{ffi, util};
+use crate::micropython::{ffi, util, Error};
#[repr(C)]
pub struct MsgObj {
@@ -80,7 +79,7 @@ impl MsgObj {
// we're returning a mutable dict.
Ok(Gc::new(Dict::with_map(self.map.try_clone()?))?.into())
}
- _ => Err(Error::AttributeError(attr)),
+ _ => Err(Error::AttributeError(attr.into())),
}
}
@@ -94,7 +93,7 @@ impl MsgObj {
self.map.set(attr, value)?;
Ok(())
} else {
- Err(Error::AttributeError(attr))
+ Err(Error::AttributeError(attr.into()))
}
}
}
@@ -235,7 +234,7 @@ unsafe extern "C" fn msg_def_obj_attr(self_in: Obj, attr: ffi::qstr, dest: *mut
}
}
_ => {
- return Err(Error::AttributeError(attr));
+ return Err(Error::AttributeError(attr.into()));
}
}
Ok(())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.