Skip to content

fix(core): avoid blocking THP when send buffer is full #5618

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions core/src/trezor/wire/thp/channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
is_there_a_channel_to_replace,
)
from trezor import protobuf, utils, workflow
from trezor.loop import Timeout
from trezor.loop import Timeout, race, sleep

from ..protocol_common import Message
from . import (
Expand Down Expand Up @@ -58,6 +58,11 @@
_MAX_RETRANSMISSION_COUNT = const(50)
_MIN_RETRANSMISSION_COUNT = const(2)

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


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

for i in range(_MAX_RETRANSMISSION_COUNT):
await self.ctx.write_payload(header, payload)
result = await race(self.ctx.write_payload(header, payload), _WRITE_TIMEOUT)
if isinstance(result, int):
if __debug__:
log.error(__name__, "Sending is stuck for %d ms", _WRITE_TIMEOUT_MS)
break

# starting from 100ms till ~3.42s
timeout_ms = round(10200 - 1010000 / (100 + i))
try:
Expand All @@ -406,7 +416,8 @@ async def write_encrypted_payload(self, ctrl_byte: int, payload: bytes) -> None:
ABP.set_send_seq_bit_to_opposite(self.channel_cache)
return

raise ThpError("Retransmission timeout")
# restart event loop due to unresponsive channel
raise Timeout("THP retransmission timeout")

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