fix(core/caesar): allow GC previous allocations at `confirm_payment_request()`
What changed, and why it matters
This commit fixes an out-of-memory (OOM) crash during Ethereum payment-request signing on the Trezor Safe 3 (T2B1). The fix wraps UI layout construction in a temporary async task so the device's garbage collector can reclaim memory sooner. It is a reliability/DoS-style bug rather than a theft-of-funds vulnerability.
Treat as a low-severity stability fix. Ensure the existing `test_signtx_payment_req` device test passes on T2B1 after the change. No urgent security response is warranted unless an attacker can craft a payment request that reliably triggers the OOM to deny service or corrupt state; if such a vector is identified, re-evaluate as a DoS issue.
Security signals we found
Out-of-memory (OOM) crash on a hardware wallet during transaction signing
Fix enables earlier garbage collection of UI allocations
Crash reproducible by an existing device test for Ethereum payment requests
No changelog entry, suggesting internal bug fix rather than security advisory
Evidence from the diff
In core/src/trezor/ui/layouts/caesar/__init__.py, confirm_payment_request() previously built menu_items and related UI objects in the same coroutine frame that later awaited confirm_trade() for each trade. On memory-constrained devices (T2B1), the retained references prevented garbage collection and caused the test test_signtx_payment_req to OOM. The patch moves the menu-building and first confirmation logic into a nested _task() coroutine; awaiting it lets MicroPython drop the local references, allowing GC to free intermediate allocations before the subsequent trade confirmations run.
Changed components
core/src/trezor/ui/layouts/caesar/__init__.pyTrezor Safe 3 (T2B1) firmwareEthereum payment-request signing flowInspect captured patch +37 / −33
diff --git a/core/src/trezor/ui/layouts/caesar/__init__.py b/core/src/trezor/ui/layouts/caesar/__init__.py
index a84788c7..89713dec 100644
--- a/core/src/trezor/ui/layouts/caesar/__init__.py
+++ b/core/src/trezor/ui/layouts/caesar/__init__.py
@@ -577,42 +577,46 @@ async def confirm_payment_request(
) as obj:
await raise_if_not_confirmed(obj, "confirm_payment_request")
- menu_items = []
- if recipient_address is not None:
- menu_items.append(
- create_details(TR.address__title_provider_address, recipient_address)
- )
- for refund in refunds:
- refund_account_items: list[StrPropertyType] = [("", refund.address, None)]
- if refund.account:
- refund_account_items.append((TR.words__account, refund.account, None))
- if refund.account_path:
- refund_account_items.append(
- (TR.address_details__derivation_path, refund.account_path, None)
+ async def _task() -> None:
+ menu_items = []
+ if recipient_address is not None:
+ menu_items.append(
+ create_details(TR.address__title_provider_address, recipient_address)
)
- menu_items.append(
- create_details(
- TR.address__title_refund_address,
- refund_account_items,
+ for refund in refunds:
+ refund_account_items: list[StrPropertyType] = [("", refund.address, None)]
+ if refund.account:
+ refund_account_items.append((TR.words__account, refund.account, None))
+ if refund.account_path:
+ refund_account_items.append(
+ (TR.address_details__derivation_path, refund.account_path, None)
+ )
+ menu_items.append(
+ create_details(
+ TR.address__title_refund_address,
+ refund_account_items,
+ )
)
- )
- if menu_items:
- menu = Menu.root(menu_items)
+ if menu_items:
+ menu = Menu.root(menu_items)
- with trezorui_api.confirm_with_info(
- title=title,
- items=[(TR.words__provider, True), (recipient_name, False)],
- verb=TR.buttons__continue,
- verb_info=INFO_ICON,
- external_menu=True,
- ) as main_layout:
- await confirm_with_menu(main_layout, menu, "confirm_payment_request")
- else:
- await confirm_properties(
- "confirm_payment_request",
- title,
- [(TR.words__provider, recipient_name, True)],
- )
+ with trezorui_api.confirm_with_info(
+ title=title,
+ items=[(TR.words__provider, True), (recipient_name, False)],
+ verb=TR.buttons__continue,
+ verb_info=INFO_ICON,
+ external_menu=True,
+ ) as main_layout:
+ await confirm_with_menu(main_layout, menu, "confirm_payment_request")
+ else:
+ await confirm_properties(
+ "confirm_payment_request",
+ title,
+ [(TR.words__provider, recipient_name, True)],
+ )
+
+ # Allow GC to free the objects allocated above.
+ await _task()
for trade in trades:
await confirm_trade(
Why this scored 22/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.