Skip to content

Fix WebSocket 64-bit payload truncation and chunked status text#37

Merged
hellerve merged 1 commit into
mainfrom
claude/fix-ws-64bit-chunked-status
Jun 21, 2026
Merged

Fix WebSocket 64-bit payload truncation and chunked status text#37
hellerve merged 1 commit into
mainfrom
claude/fix-ws-64bit-chunked-status

Conversation

@carpentry-agent

Copy link
Copy Markdown
Contributor

Summary

Two bug fixes:

WebSocket decode-frame 64-bit payload length truncation (RFC 6455 §5.2)

When the payload length indicator is 127 (64-bit extended length), decode-frame only read bytes 6–9 (the low 32 bits), ignoring bytes 2–5 (the high 32 bits). A remote peer sending a frame header with non-zero high bytes would cause the decoder to compute a wrong payload length, potentially desynchronizing the frame parser.

The fix checks the high 4 bytes via bit-or; if any are non-zero the payload exceeds the 32-bit Int range and the frame is rejected (returns Nothing). When all high bytes are zero, the existing low-32-bit calculation is used unchanged.

Response.chunked hardcoded status text

Response.chunked always passed "OK" as the reason phrase regardless of the code parameter, so (Response.chunked 404 ...) would produce HTTP/1.1 404 OK on the wire. Now uses Status.reason from the http library to derive the correct phrase.

Test plan

  • New test: 64-bit length high bytes are not truncated — crafts a raw frame header with byte 5 = 1 (plen = 2³²), verifies decoder returns Nothing
  • New test: 64-bit frame round-trips with correct payload length — 65536-byte payload triggers the 64-bit path, verifies encode/decode round-trip
  • New test: encode-text uses 64-bit length for >= 65536 bytes — verifies the length indicator byte is 127
  • New tests: chunked 200/404/500 has reason phrase ... — verifies correct reason phrase for different status codes
  • All 97 web tests pass, 112/115 websocket tests pass (3 pre-existing SHA1 endianness failures on ARM)

Opened by the carpentry-org heartbeat agent (Claude). Veit Heller has not reviewed this yet.

…nked status text

decode-frame silently ignored the high 4 bytes of 64-bit extended
payload lengths (RFC 6455 §5.2), reading only the low 32 bits.  A
remote peer sending a frame header with non-zero high bytes would
cause the decoder to compute a wrong payload length.  The decoder
now checks the high bytes and rejects frames whose payload length
exceeds the 32-bit Int range.

Response.chunked always set the reason phrase to "OK" regardless of
the status code.  Now uses Status.reason from the http library.

@carpentry-reviewer carpentry-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Build & Tests

  • Build: fails on ARM Linux (pre-existing tm_zone const-correctness issue — identical failure on main)
  • Tests: CI passes on macOS (the only CI platform for this repo)
  • CI: pass (macOS)

Findings

No issues found. Both fixes are correct and well-tested.

WebSocket 64-bit payload length (RFC 6455 §5.2):

  • Byte ordering is correct: offsets 2–5 are the high 32 bits, offsets 6–9 are the low 32 bits, read in big-endian order.
  • The bit-or rejection check correctly detects any non-zero high byte. Since Carp uses 32-bit Int, rejecting payloads > 2³² with Nothing is the right approach.
  • Low-32-bit reconstruction (byte6 * 2²⁴ + byte7 * 2¹⁶ + byte8 * 2⁸ + byte9) is correct big-endian.
  • Buffer bounds are checked: (< avail 10) guard at the top ensures all offsets 2–9 are safe to read.
  • Test at websocket.carp crafts a header with byte 5 = 1 (high word = 2³²), verifying the old code would have read plen=0 but the fix now returns Nothing. Round-trip test with 65536-byte payload confirms normal 64-bit frames still work.

Response.chunked status text:

  • Status.reason exists in the http library (loaded as http@0.1.4) and maps status codes to standard reason phrases with an "Unknown" fallback.
  • Tests verify 200 → "OK", 404 → "Not Found", 500 → "Internal Server Error".

Formatting: the large re-indentation blocks in handle-writable-ws, handle-readable-ws, and handle-readable are pure whitespace changes — logic is identical.

CHANGELOG: entries accurately describe both fixes.

Verdict: merge

Both fixes address real bugs (RFC 6455 violation for >4 GiB frames, incorrect HTTP status lines for non-200 chunked responses). Well-tested and correctly implemented.

@hellerve hellerve merged commit 8d1fcae into main Jun 21, 2026
1 check passed
@hellerve hellerve deleted the claude/fix-ws-64bit-chunked-status branch June 21, 2026 19:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant