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

Jens Geyer resolved THRIFT-6243.
--------------------------------
    Fix Version/s: 0.25.0
         Assignee: Jens Geyer
       Resolution: Fixed

> Grow the TNonblockingServer read buffer as the payload arrives, not on the 
> frame header
> ---------------------------------------------------------------------------------------
>
>                 Key: THRIFT-6243
>                 URL: https://issues.apache.org/jira/browse/THRIFT-6243
>             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
>
> {{TNonblockingServer}}'s {{TConnection}} reserves the whole declared frame 
> the moment it reads the four-byte length prefix: {{transition()}} in the 
> {{APP_READ_FRAME_SIZE}} state reallocates the read buffer to the frame size 
> ({{TNonblockingServer.cpp}}) before any payload byte has arrived. A peer can 
> therefore make the server reserve up to {{getMaxFrameSize()}} bytes per 
> connection by sending only a four-byte header, with {{MAX_CONNECTIONS}} 
> defaulting to {{INT_MAX}}. The reservation is address space rather than 
> resident memory (it is a {{std::realloc}}), but it is still an unamplified 
> header-to-allocation step.
> This is part (4) of the nonblocking-server frame handling. THRIFT-6183 
> lowered the default limit; a separate change wires the server to a 
> {{TConfiguration}} and adds a pre-allocation budget check. This change bounds 
> the allocation by what the peer actually sends:
> * {{transition()}} reserves only enough to hold the length prefix and begin 
> reading (the prefix plus about a kilobyte), not the declared frame.
> * The buffer grows toward the frame size in the {{SOCKET_RECV}} state as 
> bytes are actually read -- by the same doubling the up-front allocation used, 
> now driven by what the peer sends rather than by the size it declares, with 
> the doubling guarded so it cannot overflow {{uint32_t}}.
> A frame that fully arrives ends at the same buffer size as before (the next 
> power of two at or above the frame size), so there is no change for 
> legitimate traffic in the steady state and no change at all for frames of a 
> kilobyte or less (still a single allocation). A header with no payload 
> reserves next to nothing.
> Tradeoff, for the reviewer: a large frame that does arrive now pays the 
> amortized doubling copy that any grow-as-you-go buffer pays -- on the order 
> of twice the frame size in {{memmove}} over the life of the connection, 
> against a single allocation before. This is the cost of not trusting the 
> declared size, and the same shape the scan that raised this recommended 
> ("allocate as the payload arrives, not on the header").
> Test: TNonblockingServerTest gains three cases. A Linux-only, forked case 
> announces a 256 MiB frame, sends none of it, and asserts the server's VmSize 
> does not grow by more than a small slack -- it reads VmSize rather than RSS 
> because the reservation is committed, not resident; it fails when the buffer 
> is reserved on the header and passes when it grows with the payload. Two 
> portable cases drive multi-megabyte requests through the framed client -- 
> each frame far larger than the initial reservation, so it is assembled over 
> many libevent callbacks with the buffer doubling as the bytes arrive, and 
> every request must round-trip byte-for-byte. One frees the grown buffer when 
> the connection closes (via {{returnConnection}}) and regrows it on the next 
> connection; the other forces the resize counter to 1 so the buffer is freed 
> between two frames on a single live connection and regrown from empty 
> mid-stream. Together they cover the grow, the free, and the regrow-from-empty 
> paths, so a lost byte or a mishandled empty buffer shows up as a mismatch.
> _Drafted with AI assistance (Claude Opus 4.8)._



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

Reply via email to