Skip to content

Commit 547cfc6

Browse files
committed
fix(core): unblock THP loop after 5s write timeout
Stop retransmission if writes are blocked - e.g. due to USB flow control. It allows restarting the event loop to handle other THP channels. `loop.race()` is used, since it schedules a single timeout task per message - instead of `loop.wait.timeout_ms`, which schedules a timeout task once per packet. [no changelog]
1 parent 62b26ca commit 547cfc6

File tree

1 file changed

+14
-3
lines changed

1 file changed

+14
-3
lines changed

core/src/trezor/wire/thp/channel.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
is_there_a_channel_to_replace,
2222
)
2323
from trezor import protobuf, utils, workflow
24-
from trezor.loop import Timeout
24+
from trezor.loop import Timeout, race, sleep
2525

2626
from ..protocol_common import Message
2727
from . import (
@@ -58,6 +58,11 @@
5858
_MAX_RETRANSMISSION_COUNT = const(50)
5959
_MIN_RETRANSMISSION_COUNT = const(2)
6060

61+
# Stop retransmission if writes are blocked - e.g. due to USB flow control.
62+
# It allows restarting the event loop to handle other THP channels.
63+
_WRITE_TIMEOUT_MS = const(5_000)
64+
_WRITE_TIMEOUT = sleep(_WRITE_TIMEOUT_MS)
65+
6166

6267
class Reassembler:
6368
def __init__(self, cid: int, read_buf: ThpBuffer) -> None:
@@ -391,7 +396,12 @@ async def write_encrypted_payload(self, ctrl_byte: int, payload: bytes) -> None:
391396
ABP.set_sending_allowed(self.channel_cache, False)
392397

393398
for i in range(_MAX_RETRANSMISSION_COUNT):
394-
await self.ctx.write_payload(header, payload)
399+
result = await race(self.ctx.write_payload(header, payload), _WRITE_TIMEOUT)
400+
if isinstance(result, int):
401+
if __debug__:
402+
log.error(__name__, "Sending is stuck for %d ms", _WRITE_TIMEOUT_MS)
403+
break
404+
395405
# starting from 100ms till ~3.42s
396406
timeout_ms = round(10200 - 1010000 / (100 + i))
397407
try:
@@ -406,7 +416,8 @@ async def write_encrypted_payload(self, ctrl_byte: int, payload: bytes) -> None:
406416
ABP.set_send_seq_bit_to_opposite(self.channel_cache)
407417
return
408418

409-
raise ThpError("Retransmission timeout")
419+
# restart event loop due to unresponsive channel
420+
raise Timeout("THP retransmission timeout")
410421

411422
def _encrypt(self, buffer: utils.BufferType, noise_payload_len: int) -> None:
412423
if __debug__:

0 commit comments

Comments
 (0)