Security audit findings NET-03 and Codex #6 ("control-frame prefixes
can reserve about 512 MiB across concurrent decoders").
Both directions of the control plane shared MAX_CONTROL_FRAME_BYTES
(8 MiB). That size exists for responses: a HelloSnapshot with 4096
library games and a maximal Call-to-Play author slice legitimately
approaches it. Requests are tiny; the largest possible GetGameFileChunk
with a 255-byte game ID and a 900-byte catalog path is under 2 KiB.
Yet every anonymous inbound stream was decoded with an 8 MiB
LengthDelimitedCodec, and tokio-util reserves the declared frame length
as soon as the 4-byte prefix arrives. With 64 global control-stream
permits a LAN host could make a responder reserve ~512 MiB by sending
nothing but length prefixes.
Changes:
- lanspread-proto gains MAX_REQUEST_FRAME_BYTES (64 KiB). Request
encode/decode enforce it in addition to the shared bound; Response
keeps the 8 MiB allowance.
- The server-side stream handler decodes inbound frames with a
request-sized codec. The response writer is unchanged.
- The server QUIC limits shrink the per-stream receive window to one
request frame and size the connection window so every one of the 32
allowed streams can hold its allowance (2 MiB per connection instead
of 8 MiB per stream).
Client-side decoders (network.rs, discovery Hello pulls) still use the
8 MiB bound because they read responses from identity-pinned peers.
Test plan: `just test` (proto tests assert the exact limits and that a
maximal request encodes far below the bound; stream tests assert the
inbound codec uses the request bound). Manual: three peer-cli
containers still exchange snapshots and complete downloads.
Claude-Session: https://claude.ai/code/session_017C3Nbgwpdm3YNwZhhFLHwg