[ 
https://issues.apache.org/jira/browse/THRIFT-6180?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Jens Geyer resolved THRIFT-6180.
--------------------------------
      Assignee: Jens Geyer
    Resolution: Fixed

> C++ WebSocket server mis-frames empty and control frames
> --------------------------------------------------------
>
>                 Key: THRIFT-6180
>                 URL: https://issues.apache.org/jira/browse/THRIFT-6180
>             Project: Thrift
>          Issue Type: Bug
>          Components: C++ - Library
>            Reporter: Jens Geyer
>            Assignee: Jens Geyer
>            Priority: Major
>             Fix For: 0.25.0
>
>          Time Spent: 20m
>  Remaining Estimate: 0h
>
> TWebSocketServer gets WebSocket framing wrong in three related places, all in 
> lib/cpp/src/thrift/transport/TWebSocketServer.h. All three are demonstrated 
> below against master.
> 1. The masking key of a zero-length masked frame is never consumed.
> readFrame reads the four masking-key bytes inside "if (length > 0)". RFC 6455 
> section 5.2 puts the masking key in every frame whose MASK bit is set, 
> whatever the payload length, and readFrame itself refuses any client frame 
> that does not set MASK. So for a zero-length frame the four bytes stay in the 
> stream and the next frame header is parsed out of the masking key instead.
> A masking key is arbitrary, so this fails in whatever way the key happens to 
> encode. With the usual 0x37373737 the connection dies at once:
>   client: 89 80 37 37 37 37        (an empty Ping)
>           82 85 37 37 37 37 ...    (a five-byte data frame)
>   server: TTransportException: Reserved bits must be zeroes
> An empty Ping, an empty Pong and a Close with no status code are all ordinary 
> traffic that browsers send, so this is reachable without anything unusual on 
> the wire.
> 2. A Pong declares a length of zero and then writes the payload after it.
> writeFrameHeader takes the length from writeBuffer_.available_read(), which 
> describes the body only for flush(), the one caller whose body is 
> writeBuffer_. pong() writes readBuffer_ instead:
>   ping of 8 bytes "PINGDATA"  ->  server writes  8A 00 50 49 4E 47 44 41 54 41
> Opcode 0x8A is FIN|Pong, the declared length is 0, and eight bytes of payload 
> follow it. A conforming client reads a zero-length Pong and then parses 0x50 
> as the next frame header, whose reserved bits are set, and must fail the 
> connection.
> 3. A Close frame does the same.
> failConnection writes the two-byte close code after a header that declares 
> zero:
>   server writes  88 00 03 F1     (0x03F1 = 1009, Message Too Big)
> So the reason the connection was closed is unreadable to a conforming peer.
> The fix gives writeFrameHeader the length it is describing rather than 
> letting it guess, so pong() and failConnection() declare what they actually 
> write, and moves the masking-key read out of the payload-length branch. A 
> frame with no payload also resets the read buffer, so that a Pong for an 
> empty Ping echoes nothing rather than whatever the previous frame left unread.
> Tests in lib/cpp/test/TWebSocketServerTest.cpp cover all three: an empty 
> masked Ping followed by a data frame is read normally, a Pong declares the 
> length it carries, and a Close declares its two bytes.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to