Skip to content

Commit 9a1ae7b

Browse files
committed
Fix some zlib problems
By investigating some 64-bit portability warnings from MSVC some bugs were found in the API wrappers for zlib's Deflate and Inflate functions. The decompressed data was leaked as the function returned and could lead to memory exhaustion in the runtime upon repeated calls, this has been addressed by using RAII to release the temporary buffer. This also fixed a bug where both C++ `new[]` and C `realloc` was used for the same storage. Some constraints were tightened to avoid problems with correctness due to unfortunate integer type sizes beyond our control in zlib.
1 parent 59492a4 commit 9a1ae7b

1 file changed

Lines changed: 34 additions & 14 deletions

File tree

ui_api.cpp

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -854,16 +854,26 @@ static int l_Deflate(lua_State* L)
854854
deflateInit(&z, 9);
855855
size_t inLen;
856856
byte* in = (byte*)lua_tolstring(L, 1, &inLen);
857-
int outSz = deflateBound(&z, inLen);
858-
byte* out = new byte[outSz];
857+
// Prevent deflation of input data larger than 128 MiB.
858+
size_t const maxInLen = 128ull << 20;
859+
if (inLen > maxInLen) {
860+
lua_pushnil(L);
861+
lua_pushstring(L, "Input larger than 128 MiB");
862+
return 2;
863+
}
864+
uLong outSz = deflateBound(&z, (uLong)inLen);
865+
// Clamp deflate bound to a fairly reasonable 128 MiB.
866+
size_t const maxOutLen = 128ull << 20;
867+
outSz = std::min<uLong>(outSz, maxOutLen);
868+
std::vector<byte> out(outSz);
859869
z.next_in = in;
860-
z.avail_in = inLen;
861-
z.next_out = out;
870+
z.avail_in = (uInt)inLen;
871+
z.next_out = out.data();
862872
z.avail_out = outSz;
863873
int err = deflate(&z, Z_FINISH);
864874
deflateEnd(&z);
865875
if (err == Z_STREAM_END) {
866-
lua_pushlstring(L, (const char*)out, z.total_out);
876+
lua_pushlstring(L, (const char*)out.data(), z.total_out);
867877
return 1;
868878
}
869879
else {
@@ -881,30 +891,40 @@ static int l_Inflate(lua_State* L)
881891
ui->LAssert(L, lua_isstring(L, 1), "Inflate() argument 1: expected string, got %s", luaL_typename(L, 1));
882892
size_t inLen;
883893
byte* in = (byte*)lua_tolstring(L, 1, &inLen);
884-
int outSz = inLen * 4;
885-
byte* out = new byte[outSz];
894+
size_t const maxInLen = 128ull << 20;
895+
if (inLen > maxInLen) {
896+
lua_pushnil(L);
897+
lua_pushstring(L, "Input larger than 128 MiB");
898+
}
899+
uInt outSz = (uInt)(inLen * 4);
900+
std::vector<byte> out(outSz);
886901
z_stream_s z;
887902
z.next_in = in;
888-
z.avail_in = inLen;
903+
z.avail_in = (uInt)inLen;
889904
z.zalloc = NULL;
890905
z.zfree = NULL;
891-
z.next_out = out;
906+
z.next_out = out.data();
892907
z.avail_out = outSz;
893908
inflateInit(&z);
894909
int err;
895910
while ((err = inflate(&z, Z_NO_FLUSH)) == Z_OK) {
911+
// Output buffer filled, try to embiggen it.
896912
if (z.avail_out == 0) {
897-
// Output buffer filled, embiggen it
898-
int newSz = outSz << 1;
899-
trealloc(out, newSz);
900-
z.next_out = out + outSz;
913+
// Avoid growing inflate output size after 128 MiB.
914+
size_t const maxOutLen = 128ull << 20;
915+
if (outSz > maxOutLen) {
916+
break;
917+
}
918+
int newSz = outSz * 2;
919+
out.resize(newSz);
920+
z.next_out = out.data() + outSz;
901921
z.avail_out = outSz;
902922
outSz = newSz;
903923
}
904924
}
905925
inflateEnd(&z);
906926
if (err == Z_STREAM_END) {
907-
lua_pushlstring(L, (const char*)out, z.total_out);
927+
lua_pushlstring(L, (const char*)out.data(), z.total_out);
908928
return 1;
909929
}
910930
else {

0 commit comments

Comments
 (0)