From ba8eb83e6cef6ac8a5d3717f24787945bcc2ad81 Mon Sep 17 00:00:00 2001 From: Roman Zeyde Date: Mon, 9 Jun 2025 16:13:57 +0300 Subject: [PATCH 1/3] refactor(core): step `loop.race()` task without `loop.schedule()` [no changelog] --- core/src/trezor/loop.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/trezor/loop.py b/core/src/trezor/loop.py index 385998fb797..860c020746b 100644 --- a/core/src/trezor/loop.py +++ b/core/src/trezor/loop.py @@ -333,7 +333,7 @@ def _finish(self, task: Task, result: Any) -> None: if not self.finished: self.finished = True self.exit(task) - schedule(self.callback, result) + _step(self.callback, result) def __iter__(self) -> Task: try: From 856d2b63cd78df08fd471d23be81970064c1c87d Mon Sep 17 00:00:00 2001 From: Roman Zeyde Date: Sun, 17 Aug 2025 10:35:44 +0300 Subject: [PATCH 2/3] test(core): allow joining debuglink task before restarting event loop [no changelog] --- core/src/apps/debug/__init__.py | 41 +++++++++++++++++++++++++++++++-- tests/ui_tests/fixtures.json | 12 +++++----- 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/core/src/apps/debug/__init__.py b/core/src/apps/debug/__init__.py index d5743141784..e38576fa734 100644 --- a/core/src/apps/debug/__init__.py +++ b/core/src/apps/debug/__init__.py @@ -79,6 +79,11 @@ async def return_layout_change( # wait for layout change while True: + if _EXIT_FLAG: + # a response will be sent after restarting the event loop + # (since `storage.layout_watcher` is set). + return + if not detect_deadlock or not layout_change_box.is_empty(): # short-circuit if there is a result already waiting next_layout = await layout_change_box @@ -428,6 +433,28 @@ async def dispatch_WipeDevice(msg: WipeDevice) -> None: async def _no_op(_msg: Any) -> Success: return Success() + _EXIT_FLAG = False + _EXIT_BOX = loop.mailbox() + _SESSION_TASK: loop.spawn | None = None + + _CLOSE_TIMEOUT_MS = const(5000) + + async def close_session() -> None: + if _SESSION_TASK is None: + return + + global _EXIT_FLAG + + _EXIT_FLAG = True + _EXIT_BOX.put(None) + if layout_change_box.is_empty(): + # make sure `DebugLinkGetState` won't get stuck + layout_change_box.put(None) + + res = await loop.race(_SESSION_TASK, loop.sleep(_CLOSE_TIMEOUT_MS)) + if res is not None: + log.error(__name__, "debuglink session is stuck") + async def handle_session(iface: WireInterface) -> None: from trezor import protobuf, wire from trezor.wire.codec import codec_v1 @@ -443,7 +470,15 @@ async def handle_session(iface: WireInterface) -> None: except Exception as e: log.exception(__name__, e) - while True: + read_wait = loop.wait(iface.iface_num() | io.POLL_READ) + + while not _EXIT_FLAG: + await loop.race(read_wait, _EXIT_BOX) + if _EXIT_FLAG: + # in case both `read_wait` and `_EXIT_BOX` are ready, + # don't handle the message and exit the loop. + break + try: try: msg = await ctx.read_from_wire() @@ -515,4 +550,6 @@ async def handle_session(iface: WireInterface) -> None: def boot() -> None: import usb - loop.schedule(handle_session(usb.iface_debug)) + global _SESSION_TASK + + _SESSION_TASK = loop.spawn(handle_session(usb.iface_debug)) diff --git a/tests/ui_tests/fixtures.json b/tests/ui_tests/fixtures.json index 9e65acf29bf..6f05e0da7df 100644 --- a/tests/ui_tests/fixtures.json +++ b/tests/ui_tests/fixtures.json @@ -800,7 +800,7 @@ "T2T1_cs_test_passphrase_bde.py::test_passphrase_prompt_disappears": "f8ff949ad62592c2e8e7034c538ec071444d850f51a0644477a0c892a5448818", "T2T1_cs_test_pin.py::test_last_digit_timeout": "e08c1377c568f3bae148014a2c445d5342947e2b204409126df9b13f42fe0036", "T2T1_cs_test_pin.py::test_long_press_digit": "69759f059e1a4b38ab908e9292620face39fee527fc4123e07f293c2d12c88d7", -"T2T1_cs_test_pin.py::test_pin_cancel": "00cd88148f3633cff32d1cf4b45515d8f5815b38839ec386334fd090d40d6b7e", +"T2T1_cs_test_pin.py::test_pin_cancel": "c5f1339f60ad984d0686eb05b2f18e758d4792e546fd86cf2523e450e3ee0c42", "T2T1_cs_test_pin.py::test_pin_change": "e8c993f44502edd539370c7e3273bdadabb456b10a04aa08bb85631aa4c5bffd", "T2T1_cs_test_pin.py::test_pin_delete_hold": "e0677ccaa31dd5798c7bca4ad495f57668064914bbe9ab8af4c1e1f8abf3aff3", "T2T1_cs_test_pin.py::test_pin_empty_cannot_send": "c686613163a308630a30e8086bd3c4aca47ea708d066c9aae48994371c30e907", @@ -855,7 +855,7 @@ "T2T1_de_test_passphrase_bde.py::test_passphrase_prompt_disappears": "5a9f803b8252df5ca01557ef1c9b0162775708dc340214e0f0964396af057fd1", "T2T1_de_test_pin.py::test_last_digit_timeout": "892b5959c689361e58c9d996567ddebcca24390f70da2f87623851c7b5628d24", "T2T1_de_test_pin.py::test_long_press_digit": "09e454075c7a6552564962c53ade8daa72da821ed00d0e1d454e06114e31abe1", -"T2T1_de_test_pin.py::test_pin_cancel": "6b4c548f7854f248ad2797f76afbc6d0bc35677184c50896c396c5b0b7796a7c", +"T2T1_de_test_pin.py::test_pin_cancel": "703652c41a354dc68d3a8b9dfea343395bd31355c07f96b9e24551fdfdf67a34", "T2T1_de_test_pin.py::test_pin_change": "1eccd53cf1bc9871e891effaa08fb6114273e6f147f2134b992a5db41a758e77", "T2T1_de_test_pin.py::test_pin_delete_hold": "3a92537f7c1c10edd01eb74b4ac12d1bb7fa62fd44bd03896b5be670e200f851", "T2T1_de_test_pin.py::test_pin_empty_cannot_send": "e84725a26b14d022a3593e4cecb73f6473180f4cdf9d5a785660ba9a6690cb70", @@ -910,7 +910,7 @@ "T2T1_en_test_passphrase_bde.py::test_passphrase_prompt_disappears": "888767326ba692427077a717c0169a903d1eae77150a0052897c2296ebc9860f", "T2T1_en_test_pin.py::test_last_digit_timeout": "6ca215c2f62c4d97c923f68c4a7293e55eaadc03df1ff089d6972d301ecfde56", "T2T1_en_test_pin.py::test_long_press_digit": "2acea84d185050fd27052c74cb5ebe901263e782167b1cf29f582b654634334e", -"T2T1_en_test_pin.py::test_pin_cancel": "6925b022e7d245154b0f2d6aa08e1e2194827e6a3b0964daa3e786c496e987b3", +"T2T1_en_test_pin.py::test_pin_cancel": "42670d998eb490ba59f126a14a2c1fc5e7ea42089242f46bee04644ffce5fa97", "T2T1_en_test_pin.py::test_pin_change": "615d2c9619afbfc6f7bd9107237f0896871f5dc12d4f7fafb517b0fef30908f7", "T2T1_en_test_pin.py::test_pin_delete_hold": "0f84c4bf4f24552bfd826db46699b053db814c9c7077fd65c63acdb004d406ef", "T2T1_en_test_pin.py::test_pin_empty_cannot_send": "126eb473b472bbd24377d68f165260427535c0ddbc20c3f8a85390f5d95b720b", @@ -965,7 +965,7 @@ "T2T1_es_test_passphrase_bde.py::test_passphrase_prompt_disappears": "0cb973a9787b3ca94f0768c03e06437d4668f9edc4909dceaa96fbbe53b4de2d", "T2T1_es_test_pin.py::test_last_digit_timeout": "24355add7b41290c9eedfe15c9b6f264636223d6d1bb2f32dc5ff3273e4c0480", "T2T1_es_test_pin.py::test_long_press_digit": "56ca6d0a6737b3a34c44db222e33c04166419a9b54a88172cfa52b5e7056ea79", -"T2T1_es_test_pin.py::test_pin_cancel": "212aae36f4359d4333db3691e8b484d4172ab124a8fe7919e5108d2ed1aacab8", +"T2T1_es_test_pin.py::test_pin_cancel": "ad1f2c76b292a5f6cdfea96ca50ef03706bf678adb6a6bb0529f3921e132447d", "T2T1_es_test_pin.py::test_pin_change": "0476cf9650fa9da961508662c64b6efa630bfd11a0748fdb4f2f655b3cc4b6f9", "T2T1_es_test_pin.py::test_pin_delete_hold": "78168fb67e05d2b365d372cbf8c36452f1bce8774133f00c84945296bcf32c4b", "T2T1_es_test_pin.py::test_pin_empty_cannot_send": "673fcf6ded02fbb7d654cd1388a5fa72741cf6a5b19578645ce3d7898fe1f6a5", @@ -1020,7 +1020,7 @@ "T2T1_fr_test_passphrase_bde.py::test_passphrase_prompt_disappears": "0e6b209df1421cb051d97c220c568405bb60bc815fe24d198f07b0a5ed52c4f7", "T2T1_fr_test_pin.py::test_last_digit_timeout": "781433513b748daf0e285e1fb1e906bfab97cddee3a94aaef0a0d09d1010599a", "T2T1_fr_test_pin.py::test_long_press_digit": "edc2379a7c23439715c780a2b51098dbbb6282b1b13ff307558a74a58a421d48", -"T2T1_fr_test_pin.py::test_pin_cancel": "591201786ba7a008a6593019b61616ce0d34f34d833d9a906ebb7a01005e8ee8", +"T2T1_fr_test_pin.py::test_pin_cancel": "69cbc1787d0aa1733f8c643dbbd93c39c00291b80660f37ef84f90a9543f2ad0", "T2T1_fr_test_pin.py::test_pin_change": "35be4e7ed394f809531f62cc01e30a9c73bb1a3462abf7586b5feea4043a613f", "T2T1_fr_test_pin.py::test_pin_delete_hold": "d10a429dc769f8e67e62a06798c50d332a9fcc76ae97acdc8e7e23554ad840e0", "T2T1_fr_test_pin.py::test_pin_empty_cannot_send": "6bc3d997f24267efe06c9ac4d37d6fadd8d18b5c9fb896095b8f2a6dfcf59265", @@ -1075,7 +1075,7 @@ "T2T1_pt_test_passphrase_bde.py::test_passphrase_prompt_disappears": "a9f60f9d7f3cd8a1daded5709366211a5a12d172057d00a1202779f21b115e3f", "T2T1_pt_test_pin.py::test_last_digit_timeout": "b08d854fdccdf4b14a1b8c6901786e798ea8078ac9c1704edb1ea7b54bf2d634", "T2T1_pt_test_pin.py::test_long_press_digit": "2f38e18ba70f161f96e6a947415a937d1792ffd7dfc6824eb1c72fbc128ff630", -"T2T1_pt_test_pin.py::test_pin_cancel": "fac3d06f69db9a73ec1162fd260406b968d71734a9a508c01ebcf9ed195cc0bc", +"T2T1_pt_test_pin.py::test_pin_cancel": "fe7bfb5174f22f11d64db6db0c3a40f7c869caa74009033185201b3ae1f4d2fa", "T2T1_pt_test_pin.py::test_pin_change": "9915bd713bd0ce7a7118123c9fbcf9c02f8c2d4b456a05551f275723f7f758ed", "T2T1_pt_test_pin.py::test_pin_delete_hold": "cf7dc3062be3a17d38e74c78de441259bd58b3ae066b0b1e42046edbee277cc3", "T2T1_pt_test_pin.py::test_pin_empty_cannot_send": "bf1b509e362ff71cbb982b320c6375173cccfcddf259f1ad48a53a5ad9f29009", From 946821c9d859115e7d80e405b2b8338bb3867132 Mon Sep 17 00:00:00 2001 From: Roman Zeyde Date: Mon, 18 Aug 2025 22:23:30 +0300 Subject: [PATCH 3/3] feat(core): join debuglink task before restarting Codec v1 event loop [no changelog] --- core/src/trezor/wire/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/src/trezor/wire/__init__.py b/core/src/trezor/wire/__init__.py index f2ff9944099..46b4607df72 100644 --- a/core/src/trezor/wire/__init__.py +++ b/core/src/trezor/wire/__init__.py @@ -167,9 +167,11 @@ async def handle_session(iface: WireInterface) -> None: if not do_not_restart: # Wait for all active workflows to finish. await workflow.join_all() - # Let the session be restarted from `main`. if __debug__: - log.debug(__name__, "loop.clear()", iface=iface) + import apps.debug + + await apps.debug.close_session() + # Let the session be restarted from `main`. loop.clear() return # pylint: disable=lost-exception