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
Encode binary in three-byte groups so the base64 output is valid; escape a
backslash on write; format an escaped control byte through the hex helper;
match the escape character on read by value rather than as a pattern; and
keep the byte a \u00XX escape decodes to. Adds a round-trip test over a
memory buffer for strings with quotes, backslashes, control bytes and
\u00XX escapes and for binary of every length and byte value.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Client: lua
Split each header line at the first ':' with a plain search and trim the
two halves with anchored patterns. Every well-formed header parses as
before; a line whose field name is not a run of name characters is no
longer taken as that header. Extends the Lua HTTP header test with
field-name and value checks and a same-length parsing-cost check.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Client: lua
readMessageBegin() reads the method name of a message without a version
header from a length the peer declares, and passed that length straight to
trans:readAll(), which reads and retains that many bytes. readString() puts
a declared length through checkStringSize() first; this one path did not, so
the configured maximum did not apply to the first field of a connection.
Route it through the same check.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Client: lua
socket_send() resumes each retry after the last byte sent and reports a
timeout if its retries run out before all of the data is sent. TSocket:write()
raises a TTransportException when the send fails, as TSocket:read() does for a
failed receive.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Client: lua
TFramedTransport and THttpTransport keep the current frame or HTTP body as
it arrived and track how much of it read() has returned.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Client: lua
TJSONProtocol holds a string, a number and a base64 value to the maximum the
binary and compact protocols apply, checking the size as the value is read,
and builds each value from pieces joined at the end, so its cost stays linear
in its length.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Nothing in CI built or ran lib/lua. build.yml had no Lua job, and the
docker images only build; a change to lib/lua got no automated coverage.
lib/lua/Makefile.am had no check target either, so make check built the
four C modules and ran nothing: none of the three scripts in
lib/lua/test had ever been executed by a build. check-local now
generates the types test_recursion_depth.lua loads from test/gen-lua/
and runs all three.
Those scripts need Lua 5.3. The stand-ins they preload for the C modules
use its bitwise operators and string.pack, so on 5.2 none of the three
even parses. configure accepted 5.2, which means the new check target
would have made make check fail on every 5.2 build. configure now
requires 5.3, the focal image installs lua5.3 instead of lua5.2, and
LANGUAGES.md gives 5.3 to 5.4 instead of 5.1.5 to 5.4 -- configure had
already rejected 5.1.
Because the scripts preload those stand-ins, they cover the .lua sources
only. The job therefore also runs the Lua client against the Lua server
through test/test.py, over the three transports and three protocols
test/tests.json already lists for Lua. That is what loads libluasocket,
libluabpack, liblualongnumber and libluabitwise: with libluabpack.so
removed, all nine cases fail. It has to go through the crossrunner,
which starts the client as soon as the server's port answers; the Lua
test server gives up after one second without a connection, so a fixed
sleep before the client would not do.
The matrix is the new floor and the version the jammy and noble images
install, 5.3 and 5.4.
The configure step checks that Lua was found, and that it is the leg's
version. configure disables a binding it cannot locate rather than
failing, and with a second Lua installed the lua alternative can point
at the other one; either way the job would go on to test something
other than what its name says.
--without-lua stays in the shared CONFIG_ARGS_FOR_LIBS. Every binding
has its flag there and each job turns its own back on, which lib-lua
now does. sca.yml keeps its --without-lua too: that list is the
bindings with no static analysis, and Lua is still one of them.
Also drops two stale comments from the ubuntu Dockerfiles. "lua doesn't
have luaL_openlib any more" was settled by THRIFT-4386, which moved the
C modules to luaL_setfuncs on 5.2 and later. "lua5.3 does not install
alternatives" no longer holds: on focal and noble alike, installing
lua5.3 provides /usr/bin/lua.
Verified on clean ubuntu:24.04 containers with the job's own steps, for
both legs: the three scripts pass and 0 of 9 cross-test cases fail.
Rerun ten more times on each leg, on 5.3 with every core busy, the
cross test did not fail once. Without Lua, and with a 5.3 interpreter
taking the lua alternative on the 5.4 leg, the job fails at configure;
with one script made to fail, at make check; with a C module missing,
at the cross test. With Lua 5.2 configure now reports the library as
not built. On ubuntu:20.04 with lua5.3, as the focal image now installs
it, make check and the cross test pass as well. make dist after make
check keeps test/gen-lua out of the tarball.
Client: lua
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Client: d,lua,py
The THRIFT-6270 sweep turned up three files that no build list references:
- lib/lua: the twelve .lua modules were only in EXTRA_DIST, so make install
delivered the C libraries and none of the Lua source. They now install into
luadir through dist_lua_DATA.
- lib/d: thrift/codegen/idlgen.d was dropped from the module list in 2014 and
never restored, though it still compiles; it is back in d_codegenmodules.
- lib/py: test/test_sasl_transport.py was named in no test list, so CI never
ran it; it is registered in the autotools and CMake test lists.
Co-Authored-By: Claude Opus 4.8 <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
_readMsg() appended four bytes at a time until it saw the blank line that
ends the header block, rescanning the whole block each time and stopping at
nothing else. A peer that never sends the blank line therefore chose both how
much was accumulated and how often it was rescanned, and one that stopped
sending left the loop turning with nothing to do.
Hold the block to a maximum, stop when the peer stops sending, and look for
the blank line only in the part that is new.
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>
Adjust what "make dist" ships in the release tarball.
Dropped (build output and generated code that a built tree leaves behind,
swept in via whole-directory EXTRA_DIST entries and pruned in dist-hook;
none of it is tracked, so a clean checkout never contained it):
- lib/haxe/test/{bin,gen-haxe}, lib/javame/gen-javame
- lib/netstd bin/obj/gen-netstd across the test and benchmark projects
- test/netstd/{Client,Server}/ThriftTest
- tutorial/netstd/Interfaces/{shared,tutorial} (tracked project
scaffolding is kept so the tutorial still builds from the tarball)
- test/cpp/gen-cpp* (nodist generated sources)
- build/__pycache__ and stray *.pyc
Also no longer shipped: the .github/ CI configuration and
doc/thrift-threat-model.md.
Added (tracked sources that were missing from EXTRA_DIST):
- lib/lua/test/{RecursionDepth.thrift,test_recursion_depth.lua}
- lib/netstd/Tests/codegen/run-NetStd-Codegen-Tests.ps1
- test/JsRecursionDepthTest.thrift
- lib/php/{phpstan.neon,phpstan-baseline.neon}
- SECURITY.md
Verified end-to-end with make dist in a clean container: 4317 -> 3480
tarball entries, and the only tracked files removed are .github/* and
doc/thrift-threat-model.md.
Client: netstd,haxe,javame,lua,php,cpp
Co-Authored-By: Claude Opus 4.8 (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>
Client: py,go,php,java,netstd,delphi,haxe,lua,swift,rb,dart,erl,rs
C++ and Node.js already enforce a 10-byte ceiling on varint reads.
This change brings all remaining runtimes into parity by raising a
protocol exception (INVALID_DATA) when more than 10 bytes are consumed
without finding a terminating byte.
Java: both fast-path and slow-path varint readers are covered.
Erlang: read_varint/3 gains a when Count < 10 guard clause; call sites
updated to propagate the error tuple rather than crashing on badmatch.
Rust: VarIntReader trait replaced with local bounded loops; zigzag
decode moved to free functions independent of the integer_encoding crate.
Ruby: both the pure-Ruby implementation and the C extension are fixed;
read_varint32 gains a separate 5-byte limit instead of delegating to
read_varint64.
Named constants replace bare integer literals in all runtimes, with a
comment explaining the derivation (ceil(64/7) = 10, ceil(32/7) = 5).
Java and Rust enforce both the 5-byte (32-bit) and 10-byte (64-bit)
limits; other runtimes enforce the 10-byte ceiling uniformly.
Tests added for py, go, php, java (all four original runtimes), plus
rb, swift, dart, and rs which have existing unit-test infrastructure.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Client: lua
Patch: longzhiri <persistentsnail@gmail.com>
This closes#2212
The oneway method's processor has no need to write the result to client, but it is necessary to return values of each handler's return.
* 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