Offered in #70 (item 3b, reverse direction). Filing so it is trackable independently.
The gap
src/net/uci/net.s's net_poll takes the SOCKET_READ response header straight into uci_poll_rem and starts copying, with no check that the returned actual_len is a value the adapter actually asked for.
It survives a bogus length only incidentally: the inner copy loop re-checks ring-full and DATA_AV on every byte, so a wild length exits early rather than running away. That is a property of the copy loop, not a validation, and it is one refactor away from not holding.
Why this is worth a real check
This firmware is documented to return nonsense lengths. Measured on U64E fw 3.14d (c64-wireguard tools/test_uci_udp_size_probe.py, 2026-08-24):
| requested |
returned |
| 600 / 768 / 1280 |
512 (remainder silently discarded) |
| 1024 / 1500 |
nothing delivered at all |
and a 1500-byte request has been observed returning 0xFFFF. A firmware that returns 0xFFFF for a 1500-byte request can return a bogus length for a 512-byte one; nothing about the 512 cap makes the response trustworthy.
c64-wireguard learned this expensively. Its PR #62 fixed a case where net_poll trusted the returned length and copied ~18 KB from udp_recv_buf through $D000 — WireGuard packet bytes were found sitting in the VIC registers, presenting as a red screen and garbled charset that looked like a video bug rather than a memory-safety bug.
The check
c64-wireguard src/net/uci/net.s, the @hdr_done block — validate before trusting, and drop rather than trim:
lda uci_read_hdr+1
cmp #>UCI_READ_CHUNK_MAX
bcc @len_ok ; hi < 2 -> certainly under the cap
bne @len_bad ; hi > 2 -> certainly over it
lda uci_read_hdr+0 ; hi == 2 -> only exactly 512 is legal
beq @len_ok
@len_bad:
lda #UCI_ERR_LONG_READ
sta net_last_error
; ...drain, do not copy
Dropping is deliberate: a trimmed datagram is a corrupt datagram, and for c64-wireguard it fails AEAD anyway. For a TCP stream the equivalent argument is that a truncated fill silently desynchronises the stream — which is the same class of defect d9cd021 chased through a ring wrap, arriving from the other end.
UCI_ERR_LONG_READ is $8A in c64-wireguard, proposed for reservation in #70 (3a) and filed against the contract as JC-000/c64-lib-contract#137.
Note on the reverse direction
d9cd021's ring-free-space clamp does not port to c64-wireguard — that adapter reads one datagram into a flat buffer behind a udp_recv_ready interlock, with no wrapping ring and no free-space computation. Detail in #70. These two fixes are complements, not the same fix seen twice.
Offered in #70 (item 3b, reverse direction). Filing so it is trackable independently.
The gap
src/net/uci/net.s'snet_polltakes theSOCKET_READresponse header straight intouci_poll_remand starts copying, with no check that the returnedactual_lenis a value the adapter actually asked for.It survives a bogus length only incidentally: the inner copy loop re-checks ring-full and
DATA_AVon every byte, so a wild length exits early rather than running away. That is a property of the copy loop, not a validation, and it is one refactor away from not holding.Why this is worth a real check
This firmware is documented to return nonsense lengths. Measured on U64E fw 3.14d (c64-wireguard
tools/test_uci_udp_size_probe.py, 2026-08-24):and a 1500-byte request has been observed returning
0xFFFF. A firmware that returns0xFFFFfor a 1500-byte request can return a bogus length for a 512-byte one; nothing about the 512 cap makes the response trustworthy.c64-wireguard learned this expensively. Its PR #62 fixed a case where
net_polltrusted the returned length and copied ~18 KB fromudp_recv_bufthrough$D000— WireGuard packet bytes were found sitting in the VIC registers, presenting as a red screen and garbled charset that looked like a video bug rather than a memory-safety bug.The check
c64-wireguard
src/net/uci/net.s, the@hdr_doneblock — validate before trusting, and drop rather than trim:Dropping is deliberate: a trimmed datagram is a corrupt datagram, and for c64-wireguard it fails AEAD anyway. For a TCP stream the equivalent argument is that a truncated fill silently desynchronises the stream — which is the same class of defect
d9cd021chased through a ring wrap, arriving from the other end.UCI_ERR_LONG_READis$8Ain c64-wireguard, proposed for reservation in #70 (3a) and filed against the contract as JC-000/c64-lib-contract#137.Note on the reverse direction
d9cd021's ring-free-space clamp does not port to c64-wireguard — that adapter reads one datagram into a flat buffer behind audp_recv_readyinterlock, with no wrapping ring and no free-space computation. Detail in #70. These two fixes are complements, not the same fix seen twice.