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

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

> Size containers from the payload rather than the declared count in the Python 
> C extension
> -----------------------------------------------------------------------------------------
>
>                 Key: THRIFT-6169
>                 URL: https://issues.apache.org/jira/browse/THRIFT-6169
>             Project: Thrift
>          Issue Type: Bug
>          Components: Python - Library
>            Reporter: Jens Geyer
>            Assignee: Jens Geyer
>            Priority: Major
>             Fix For: 0.25.0
>
>          Time Spent: 20m
>  Remaining Estimate: 0h
>
> The C extension sizes a list, tuple or set from the element count the peer 
> declares, before any of the elements have been read 
> (lib/py/src/ext/protocol.tcc, the T_LIST/T_SET case of decodeValue):
>     bool use_tuple = type == T_LIST && parsedargs.immutable;
>     ScopedPyObject ret(use_tuple ? PyTuple_New(len) : PyList_New(len));
> The only check on len is checkLengthLimit(len, containerLimit()), and 
> containerLimit_ defaults to INT32_MAX (protocol.h, and again in module.cpp as 
> the fallback when the protocol's container_length_limit attribute is None, 
> which is its documented default). Measured with tracemalloc on CPython: a 
> nine byte body declaring 2,000,000 elements reserves 16 MB, and 20,000,000 
> reserves 152 MB.
> This is the same defect as THRIFT-3175, which was fixed in 0.9.3 by a hard 
> cap of 10,000 elements. That cap turned out to reject legitimate large 
> containers -- see jrf's comment on THRIFT-3175 -- and THRIFT-3532 lifted it 
> in 0.10.0 in favour of the configurable container_length_limit, whose default 
> is unlimited. So the count has been taken at face value again since 0.10.0 
> for anyone who does not set that limit.
> The fix implements the heuristic dvirsky proposed on THRIFT-3175 in 2015: 
> size the container from what the message can actually supply. Every element 
> occupies at least one byte on the wire whatever the protocol, so the bytes 
> still in the decode buffer bound how many can arrive. A message that is 
> already buffered whole -- a memory buffer, or a frame -- always has at least 
> as many bytes left as elements declared, so it is still allocated at full 
> size in one go and nothing about the common path changes. Anything else grows 
> as the elements materialise. Neither of the two earlier failure modes 
> returns: large containers are not refused, and the declared count is not 
> trusted.
> The pure-Python decoder is not affected; it appends in a loop and never 
> preallocates. Of the other native accelerated implementations in the tree, 
> Ruby's already caps this (new_container_array in lib/rb/ext/struct.c) and 
> PHP's uses array_init with no size hint.



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

Reply via email to