Files
alkcall/docs/reviews/consumer-findings-ledger.md
T
glm-5.3-flash 84fe94c4d7 docs(ledger): file CF-002 — demux TooLarge skip allocates peer-declared length
Cross-crate finding from alkhttp Review 001 (WS-12), filed here per the
ledger's purpose (consumer-surfaced alkcall findings).

src/channels/adapter.rs:144: the ChunkError::TooLarge arm allocates
vec![0u8; length] from the peer's untrusted 8-byte header before
reading; length is u32, so ~4 GiB can be pinned per connection and held
indefinitely by a dribbling peer. Reachable via the alkhttp WS path by
any authenticated browser. Skip/resync logic is correct; the memory
shape is wrong — stream-skip with a bounded buffer instead.

The normal payload arm (:159) is safe (parse_header bounds it at
MAX_CHUNK_LEN); only the TooLarge arm is unbounded.
2026-08-30 06:16:30 +00:00

3.7 KiB
Raw Blame History

Consumer findings ledger — alkhttp consuming alkcall

Findings from writing the first real consumer (alkhttp) against alkcall. alkcall is deliberately minimal; expect missing features and edge-case bugs to surface here first. Extract items into alkcall tasks/reviews as the project sees fit.

Format: date | found-in (alkhttp context) | severity | status.


Open

CF-002 — demux TooLarge skip allocates the full peer-declared length up front (up to ~4 GiB from an 8-byte header) (2026-08-30)

  • Found in: alkhttp Review 001, finding WS-12 (cross-crate; docs/reviews/001-initial-implementation-review.md, Part B, verified against tree 4a825d3). Filed to this ledger 2026-08-30. src/channels/adapter.rs:144 (run_demux_loop_for_client) — when a parsed chunk header carries length > MAX_CHUNK_LEN, the ChunkError::TooLarge arm allocates let mut discard = vec![0u8; length as usize]; before reading: the buffer is sized from the peer's untrusted 8-byte header, and length is a u32 (0xFFFFFFFF ≈ 4 GiB).
  • Impact: memory-shape DoS, not a correctness bug (the resync/skip itself is correct — alkcall's own resync test covers it). Any authenticated peer can send header [ch][len = 0xFFFFFFFF] and then dribble; the allocation is pinned for as long as the payload takes to arrive (a dribbling peer can hold it indefinitely). Via the alkhttp WS path this is trivially reachable by any authenticated browser; K concurrent connections × 4 GiB headers → OOM before the dribble even matters. The normal payload arm (:159, vec![0u8; header.length as usize]) is safe — parse_header rejects length > MAX_CHUNK_LEN, so it is bounded at 16 MiB; only the TooLarge skip arm is unbounded.
  • Suggested direction: stream-skip with a bounded fixed buffer (e.g. a 64 KiB stack/fixed buffer, read_exact loop consuming length bytes) instead of the pre-sized allocation. Optionally bound cumulative skipped bytes per connection before tearing it down, so a peer cannot loop TooLarge headers to burn bandwidth/CPU forever.
  • alkhttp side: nothing to change there — this is demux-internal. alkhttp's own read-side hardening (review-001-ws-pump-consolidation: explicit inbound max_message_size/max_frame_size, idle-read timeout) bounds the WS ingress, but a header that survives those caps still hits this arm, so the fix belongs on the alkcall side.
  • Status: open — no alkcall change made.

CF-001 — call_single_stream write-failure maps to non-retryable INTERNAL (2026-08-29)

  • Found in: alkhttp from_wss drop-monitor race tests (WS-02/CON-02, review-001-ws-eof-signal). When the transport mux dies mid-call, the write path fails fast and alkcall's call_single_stream write-failure mapping produces a non-retryable INTERNAL: failed to write request frame — even though the call never reached the producer and a reconnect/retry would be safe.
  • Impact: consumers that drop connections under load (WS EOF, network flaps) get non-retryable errors for in-flight calls that are provably not processed. Callers must either tolerate-or-discriminate two envelope shapes for the same transport-death race, or miss retry opportunities.
  • Suggested direction: classify write failures that occur before any response frame could arrive as retryable (CONNECTION_CLOSED or equivalent) — or expose the distinction so consumers can decide.
  • alkhttp side: the race test tolerates both outcomes for now (asserts retryable only when the call is provably in-flight). If/when alkcall fixes the mapping, alkhttp's ws-eof-signal test should tighten back to retryable-only.
  • Status: open — no alkcall change made.

Resolved

(empty)