fix(lf): flush stale input before bulk download (desync -> garbage/DEADBEEF)
A real download came back as BigBuf memory (0xDEADBEEF poison) with a PM3 response magic (0x62334d50) embedded — the classic signature of a desynced read: the stream had stale bytes (a prior partial transaction, or the C client having touched the port), and the bulk reader consumes fixed 544-byte OLD frames that carry no magic to resync on, so once misaligned it stays misaligned and reads memory as "samples". transport.download_bulk now flushes the input first (reset_input_buffer + drain the asyncio StreamReader until an 80ms quiet gap), so the transfer always begins byte- aligned. Drains no-op when disconnected. Test asserts the flush runs before the request. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -133,3 +133,21 @@ def test_download_bulk_uses_oldarg_length_not_padding():
|
||||
t = _transport_with([_bigbuf_frame(0, b"\xAA\xBB\xCC"), _ack()])
|
||||
got = _run(t.download_bulk(Cmd.DOWNLOAD_BIGBUF, 0, 3, Cmd.DOWNLOADED_BIGBUF, Cmd.ACK))
|
||||
assert got == b"\xAA\xBB\xCC" # trimmed to oldarg length, not 512 of padding
|
||||
|
||||
|
||||
def test_download_bulk_drains_stale_input_first():
|
||||
# a bulk download must flush stale bytes before sending, so OLD frames start byte-aligned
|
||||
payload = bytes(range(100))
|
||||
t = _transport_with([_bigbuf_frame(0, payload), _ack()])
|
||||
calls = []
|
||||
async def fake_drain(quiet=0.08):
|
||||
calls.append("drain")
|
||||
return 7
|
||||
orig_send = t.send_frame
|
||||
async def tracking_send(frame):
|
||||
calls.append("send")
|
||||
return await orig_send(frame)
|
||||
t._drain_input = fake_drain
|
||||
t.send_frame = tracking_send
|
||||
_run(t.download_bulk(Cmd.DOWNLOAD_BIGBUF, 0, 100, Cmd.DOWNLOADED_BIGBUF, Cmd.ACK))
|
||||
assert calls[0] == "drain" and calls[1] == "send" # drained BEFORE the request went out
|
||||
|
||||
Reference in New Issue
Block a user