fix(core): loop.mailbox exception handling
What changed, and why it matters
This commit fixes how a low-level task mailbox in the Trezor firmware handles stored values. Previously, if another task placed an exception into the mailbox, the waiting task would receive that exception as if it were a normal value instead of having it raised. The patch makes the mailbox re-raise stored exceptions so they propagate correctly through the async loop. This is a correctness fix in the firmware's task scheduler; it is not obviously exploitable as a security vulnerability on its own, but mishandled exceptions could in theory mask errors or alter control flow in security-critical code paths.
Treat as a firmware correctness fix. Review callers of loop.mailbox and any code that deposits exceptions into mailboxes to confirm no security-sensitive error paths were being silently converted into normal values. No immediate exploit mitigation is indicated by the diff alone.
Security signals we found
Exception value returned as normal result instead of being raised
Scheduler/task mailbox semantics inconsistent with try/except path
Potential masking of error conditions in coroutine-based firmware
No changelog entry supplied despite behavioral fix
Evidence from the diff
In core/src/trezor/loop.py, the mailbox Syscall’s value retrieval now checks whether the stored value is a BaseException. If so, it raises it; otherwise it returns the value. Before the change, a value of type BaseException placed in self.value would be returned to the caller as a normal result, bypassing exception propagation. The surrounding code already catches exceptions in a try/except and stores them in the mailbox, so the missing re-raise was an inconsistency in the coroutine scheduler’s exception handling. The patch restores normal async exception semantics.
Changed components
core/src/trezor/loop.pytrezor.loop.mailbox SyscallMicroPython async scheduler / task mailboxInspect captured patch +6 / −3
diff --git a/core/src/trezor/loop.py b/core/src/trezor/loop.py
index f3c9e3ba..5dbb34c8 100644
--- a/core/src/trezor/loop.py
+++ b/core/src/trezor/loop.py
@@ -447,9 +447,12 @@ class mailbox(Syscall[T]):
if not self.is_empty():
value = self.value
self.clear()
- # XXX self.value is of type T | object, but we are returning it as T
- # what we want to say is `assert isinstance(value, T)` but that doesn't work
- return value # type: ignore [Type "T@mailbox | object" is not assignable to type "T@mailbox"]
+ if isinstance(value, BaseException):
+ raise value
+ else:
+ # XXX self.value is of type T | object, but we are returning it as T
+ # what we want to say is `assert isinstance(value, T)` but that doesn't work
+ return value # type: ignore [Type "T@mailbox | object" is not assignable to type "T@mailbox"]
# otherwise, wait for a value
try:
Why this scored 41/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.