Skip to content

fix: add C >= 128 guards + fallback to all UTF-8 continuation validators in cow_ws (crash on malformed text frames)#160

Closed
dl-alexandre wants to merge 1 commit into
ninenines:masterfrom
dl-alexandre:fix/ws-utf8-validation-158
Closed

fix: add C >= 128 guards + fallback to all UTF-8 continuation validators in cow_ws (crash on malformed text frames)#160
dl-alexandre wants to merge 1 commit into
ninenines:masterfrom
dl-alexandre:fix/ws-utf8-validation-158

Conversation

@dl-alexandre

Copy link
Copy Markdown

Summary

WebSocket text/close frames must be valid UTF-8. The state machine in cow_ws:validate_text/2 (and validate_sN helpers) assumed continuation states (s2-s8) would only ever see bytes >= 128.

A low byte (< 128) while in one of those states (e.g. truncated/malformed frame from network or adversarial input) caused element(C-127, utf8_class()) to receive an out-of-range index and crash (badarg; after partial guard also function_clause).

This is the root cause of the crash reported in #158.

Fix (smallest defensible change)

  • Add when C >= 128 guard to the main clause of validate_s2 .. validate_s8 (consistent with validate_s0).
  • Add a fallback clause for any other non-empty input in those states: validate_sN(<<,/bits>>) -> 1; (return the 'bad encoding' sentinel).
  • The <<>> terminal clauses are unchanged (return the state number for 'more').

Valid UTF-8 paths (ASCII fast path, 2/3/4-byte sequences, fragmented text frames) are unaffected.

Testing

Local verification (no full erlang.mk TEST_DEPS fetch required):

  • erlc compiles the module cleanly (no warnings after anon-var cleanup).
  • Exercised the paths via the exported parse_payload/9 (which drives validate_payload -> validate_text for text frames):
    • ASCII and valid multi-byte (e.g. cent 0xC2A2) -> {ok, Payload, 0, <<>>}
    • Partial first-byte + second -> more then {ok}
    • Low ASCII byte while Utf8State=2 (s2) -> {error, badencoding} with zero crash.

This makes the UTF-8 validator robust against the reported class of input.

Fixes #158


(Contrib loop: claimed via work claim, investigated, smallest targeted change + manual property-like smoke on the exact crash scenario, self-verified compile+exec, now gating.)

…-s8) in cow_ws

Prevents crash (badarg in element/2) on malformed or malicious WebSocket text frames containing out-of-range continuation bytes.

This is the root cause of the UTF8 validation crash reported in ninenines#158.

All states now consistently guard like validate_s0 does, and validate_ascii already does <128 fast path.

Refs ninenines#158
@essen

essen commented Jun 4, 2026

Copy link
Copy Markdown
Member

Thanks but #159 already has the fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Crash in WebSocket UTF8 code

2 participants