Jens Geyer created THRIFT-6177:
----------------------------------

             Summary: Bound the WebSocket frame payload length before it sizes 
the read buffer in the C++ library
                 Key: THRIFT-6177
                 URL: https://issues.apache.org/jira/browse/THRIFT-6177
             Project: Thrift
          Issue Type: Bug
          Components: C++ - Library
            Reporter: Jens Geyer
             Fix For: 0.25.0


TWebSocketServer::readFrame (lib/cpp/src/thrift/transport/TWebSocketServer.h) 
sizes its read buffer from the payload length the frame header declares, before 
any payload byte has arrived:

    // size_t is smaller than a ulong on a 32-bit system
    if (payloadLength > UINT32_MAX) {
      failConnection(CloseCode::MessageTooBig);
      return false;
    }
    auto length = static_cast<uint32_t>(payloadLength);
    if (length > 0) {
      ...
      readBuffer_.resetBuffer(length);
      uint8_t* buffer = readBuffer_.getWritePtr(length);
      read = transport_->read(buffer, length);

UINT32_MAX is the only bound, so the fourteen bytes of a frame header carrying 
a 64-bit length decide the size of the allocation. Measured on a Linux x86-64 
build: a 14-byte header declaring 0xFFFFFFFF takes the process from a VmPeak of 
10,512 kB to 4,205,092 kB and asks the transport underneath for 4,294,967,295 
bytes, with no payload sent at all. resetBuffer() constructs a fresh 
TMemoryBuffer of that size, so the allocation happens whether or not the bytes 
ever arrive, and the buffer is a member that lives as long as the connection.

The transport has a TConfiguration and consults neither of its limits on this 
path.

The fix holds the declared length to TConfiguration::getMaxFrameSize() and 
refuses anything above it with close code 1009 Message Too Big, which is 
already what this transport sends when a frame is too large for it (the 
UINT32_MAX arm). That is the same ceiling TFramedTransport::readFrame applies 
to its own frames in lib/cpp/src/thrift/transport/TBufferTransports.cpp.

Compatibility, worth a release note: maxFrameSize defaults to 16384000 bytes, 
so a peer sending a single WebSocket frame larger than that is now refused 
where it was accepted before. Operators who need larger frames raise 
TConfiguration::maxFrameSize; the bound follows whatever they set.

Tests are in lib/cpp/test/TWebSocketServerTest.cpp, the first tests this 
transport has had. They assert the largest read the server asked of the 
transport underneath it rather than merely that the read failed: a payload that 
never arrives ends the frame either way, so "did it fail?" passes on the 
unmodified library too. Two of the six cases fail before the change (67108864 > 
1024 and 32768 > 1024) and all six pass after; the other four are regression 
guards that pass either way.




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

Reply via email to