Client: lua
The binary, compact and JSON protocols refused only a negative list, set or
map size, and the generated code reads one element per declared element.
Lua has no MaxMessageSize that would bound the count otherwise, so the new
limit gets a finite default instead of the "no limit of its own" default
THRIFT-6291 gives MaxContainerSize.
- TProtocol.lua: DEFAULT_MAX_CONTAINER_SIZE = 16384000, the value of the
string and frame size limits, and TProtocolBase:checkContainerSize().
maxContainerSize on a protocol instance overrides the default; zero or
less switches the limit off.
- readListBegin, readSetBegin and readMapBegin of TBinaryProtocol,
TCompactProtocol and TJSONProtocol call it before any element is read.
- doc/specs/thrift-tconfiguration.md records the finite Lua default and the
reason.
- test/test_container_limits.lua covers the three protocols for list, set
and map, at the limit and one over it, the default, the off values and
skip. Its pure-Lua stand-in for libluabpack gains the int8_t wrap of 'c'
and toVarint32, which the compact protocol needs.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Client: lua
__readFrame passed the four-byte frame length to readAll unchecked, readString
and readBinary did the same with a field length, and THttpTransport did the
same with Content-Length. All three now refuse a negative or over-maximum
length before reading anything on account of it, against a default of 16 MB
that other bindings use and that each transport or protocol can override.
readAll accumulated with buf = buf .. chunk, which builds a new string and
copies everything read so far on every pass. A peer that drips bytes therefore
made the cost quadratic in what it sent -- measured here at 29x the time for 4x
the input, against 3.9x once the chunks are collected and joined once.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Client: lua
TProtocolBase:skip descends through nested structs, lists, sets and maps, but
did not take part in the depth budget that generated read/write draws on, so the
limit only ever reached the fields a spec declares. Charge a level for each skip
and release it on the way out, including when the skip itself raises.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Client: lua
The Lua generator wraps each generated read()/write() body with
incrementRecursionDepth()/pcall/decrementRecursionDepth() on TProtocolBase
(limit DEFAULT_RECURSION_DEPTH = 64, TProtocolException.DEPTH_LIMIT on
excess). Previously only skip() was bounded, so the generated read/write
path recursed without limit. Unions and exceptions are generated through the
same path, so they are bounded too.
Replace the isolated counter test with a generated-code round-trip
(lib/lua/test/test_recursion_depth.lua) over a recursive struct (RecTree),
union (RecUnion) and exception (RecError) from a new RecursionDepth.thrift,
driven through the generated read()/write() over a real TBinaryProtocol: a
chain at the limit round-trips (also proving the guard does not double-count),
a chain past it is rejected on write, and a hand-serialized over-limit payload
is rejected on read (crafted with the real recursive field, id 1 = list<self>,
so the reader recurses through the guarded path rather than the separate,
unbounded skip()).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* THRIFT-4024: make c_glib throw on unsupported type when skipping
* THRIFT-4783: throw on invalid skip (py)
* THRIFT-4024: make cpp throw on unsupported type when skipping
* THRIFT-4024: uniform skip behavior on unsupported type