uci: bound net_poll's copy by the request; $8A is UDP-only, $8B breadcrumb (#140) - #143
Draft
JC-000 wants to merge 4 commits into
Draft
uci: bound net_poll's copy by the request; $8A is UDP-only, $8B breadcrumb (#140)#143JC-000 wants to merge 4 commits into
JC-000 wants to merge 4 commits into
Conversation
… §13, #70) The header used to be documentation: nothing included it, its declared surface overlapped the used one in 6 of 17 symbols, and the ip65 adapter had no error channel. Now every network-touching TU (boot, http, tls_record_io, tls13) `.include`s it and imports no `net_*` directly, so both backends must export the whole core/TCP/DNS surface or the link fails by name. ip65 (the tight one, LOADER had 16 B free): - net_dhcp -> net_dhcp_acquire; net_local_ip / net_resolved_ip copied from ip65_cfg_ip / ip65_dns_ip_addr on success; net_last_error with NET_ERR_IP65_* ($41-$45, ip65_errors.inc, the §13.2 ip65 range); net_tcp_state per §13.1. - net_print_ip and net_recv_ready left the adapter (below), which is what paid for the ~95 B of new logic in place. LOADER ends with 58 B free instead of 16. - net_tcp_recv_cb / net_save_zp / net_restore_zp un-exported (§13.5). - net_manifest.s: NET_BACKEND_FAMILIES = CORE|TCP|DNS plus the §13.7 blob equates, with LIB_NET_IP65_BLOB_SIZE link-asserted against ip65_blob_end - ip65_blob_start (labels added around the .incbin). UCI: - net_dhcp alias, net_tcp_set_recv_cb stub and net_print_ip deleted; UCI_TCP_* -> NET_TCP_* in code (aliases kept in uci_errors.inc); ring-full now sets tcp_recv_overflow (§13.3); $8A UCI_ERR_LONG_READ reserved with c64-wireguard's meaning, not emitted. - net_manifest.s: CORE|TCP|DNS (DNS by deferral). Consumer side: - src/net/net_families.inc (contract block verbatim, never exported), src/net/net_states.inc (NET_TCP_* values), src/net_abi_asserts.s (§13.8 family asserts + the §13.3 ring-mask assert moved out of lib_contract_asserts.s). - print_local_ip in boot.s replaces the two identical per-backend net_print_ip routines (§13.1 "deliberately not in the contract"), reading the now-ABI net_local_ip; it rides LOADER_OVERFLOW so the ip65 LOADER region gains rather than loses. Cost: the ip65 HTTPS_HOST/HTTPS_PATH tail budget is ~60 B beyond the defaults now; the wikipedia target (+46 B) still builds on both ip65 profiles. - NET_BACKEND_FAMILIES exported `: absolute` — a byte-sized equate otherwise infers zeropage and ld65 warns (contract #74 recurring). Evidence: - ip65-plain, uci-plain, ip65-onchip+wikipedia, uci-comb+wikipedia+ HTTPS_BODY_TO_REU all link clean, no ld65 warnings; PRG sizes unchanged (47,105 / 62,977 B). - VICE: test_net.py 65/65, test_http.py 61/61, test_tls_handshake.py 21/21. - U64E (uci-onchip, 48 MHz): boot_check PASS; phase2_check PASS — DHCP through net_dhcp_acquire, net_local_ip = 10.43.23.81 read back over DMA, net_last_error = 0. rig_https_local FAILs on this branch AND on master ab5de2b identically (name check vs the IP-as-hostname the rig sends) — filed separately; see the PR for the live-server run used instead. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…crumb (#140) net_poll took the SOCKET_READ response header straight into uci_poll_rem and copied, surviving a wild header only because the copy loop re-checks ring-full and DATA_AV per byte. Measured on a U64E (fw 3.14d, real github.com session): a 512 B request with ~690 B queued is answered with header $FFFF; a 22 B request with 22 B queued with $0016. On TCP the header is a count only when the queue fits the request, and the $FFFF sentinel otherwise. So c64-wireguard's drop-on-over-claim check does not port: a first cut of it aborted every real handshake at the first read (two runs). What lands instead is a bound — the copy count is capped at uci_req_len, the request this poll actually made; bytes beyond it were never delivered and stay queued, so nothing is lost. $8A UCI_ERR_LONG_READ keeps c64-wireguard's datagram disposition and is never emitted here. A header above the request that is NOT $FFFF — a value the firmware has no documented mode for — leaves $8B UCI_ERR_BAD_READ_HDR as a best- effort breadcrumb (C=0, copy still capped, §13.1 short-write pattern), allocated in c64-lib-contract SPEC §13.2's table (#139) before use. Evidence: cap-only build PASS on the U64E at 48 MHz, github.com HTTP 200, CFIN 48.5 s, PRG eccb7ec7. The cap+$8B build (2396d97d) hit $88 NO_SOCKET at TCP_CONNECT 2/2 — the device's after-N-loads connect degradation, before net_poll runs — so its twelve added bytes are hardware-unverified today; stated on the PR rather than claimed. rig_https_local/_live now dump uci_req_len/uci_read_hdr post-mortem. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
JC-000
marked this pull request as draft
August 24, 2026 18:06
…omments and docs corrected (#140) Code unchanged (byte-identical PRG): the cap plus the DATA_AV check already copy zero bytes on the sentinel. What was wrong was every comment saying $FFFF meant 'more than you asked': the first-poll $FFFF was an idle poll waiting for ServerHello, as c64-wireguard's idle-UDP measurement (11/11 sizes) makes clear. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s been observed (#140) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
DRAFT until #142 merges — this branch pins a snapshot of #142; if #142 is amended in review, this branch must be rebased before merge (merging it first would land both commits under this title and leave #142 empty). After #142 merges, check with
git merge-base --is-ancestor origin/fix/140-socket-read-header-bound origin/masteronce this one merges too.Closes #140. Stacked on #142 (same file) — merge #142 first.
What the measurements said
Porting c64-wireguard's drop-on-over-claim check verbatim aborted every real github.com handshake at the first read (2/2). With the request/claim pair added to the rig's post-mortem dump:
uci_req_lenuci_read_hdr$0200$FFFF$0016$0016Correction (same evening):
$FFFFis the firmware's no-data sentinel on both transports — c64-wireguard measured every idle UDP poll returning it (11/11 sizes), andtls_last_state=2means our first-poll capture was an idle poll waiting for ServerHello ("~690 B queued" was an inference). The drop-and-error cut aborted on an empty read; c64-wireguard had carried the same misfiling for four days as a documented firmware quirk.$FFFFmust be excluded before any over-claim test. The code here already does the right thing (cap, then theDATA_AVcheck copies zero bytes) — commitcfebf5fcorrects the comments only, PRG byte-identical.What lands
uci_poll_rem = min(header, uci_req_len). Bytes beyond the request were never delivered and stay queued for the next poll, so nothing is lost; the copy can no longer run away on a bogus header regardless of what the per-byte checks do.$8A UCI_ERR_LONG_READkeeps c64-wireguard's datagram disposition (excess discarded by firmware → datagram unrecoverable → dropped) and is never emitted on TCP. Registry row reworded by disposition in c64-lib-contract#139.$8B UCI_ERR_BAD_READ_HDR(new): header above the request that is not the$FFFFno-data sentinel — a value the firmware has no documented mode for; the stream-family counterpart of$8A(datagram: drop). Best-effort breadcrumb on the §13.1 short-write pattern: C=0, copy still capped, code left innet_last_errorfor post-mortems. Allocated in SPEC §13.2's table before the adapter emits it — the first code through the rule the table exists for._dump_diagprintsuci_req_len/uci_read_hdr.Evidence
eccb7ec7…): U64E 48 MHz,rig_https_livegithub.com PASS, HTTP 200, CFIN 48.5 s.$8Bbuild (2396d97d…):$88 NO_SOCKETatTCP_CONNECT, 2/2,tls_last_state=0,uci_req_len=$0000— the socket never opened, sonet_pollnever ran. This is the device's after-N-loads connect degradation (~14 loads today; c64-wireguard measured P P P P P T T T on unmodified master), not the change, which is unreachable without a socket. The twelve added bytes are hardware-unverified today; a power cycle and one run will close that. Both builds link clean, no warnings.a6d2d3b1…) is the negative control: it FAILs with$8Aattls_last_state=2every time.🤖 Generated with Claude Code