diff options
author | Erlang/OTP <otp@erlang.org> | 2021-09-02 12:15:37 +0200 |
---|---|---|
committer | Erlang/OTP <otp@erlang.org> | 2021-09-02 12:15:37 +0200 |
commit | a81d70bd010c032b0c49b05dc4d2eaa747852b06 (patch) | |
tree | 6c04b3b842bbe82c1783ebdc86f4b9b9631638ce | |
parent | f3a740a81d4f2ece608058706404c088e0afd5d1 (diff) | |
parent | 03e3dae2ee58b0ec0a5096c856e8a68ac4d54f05 (diff) | |
download | erlang-a81d70bd010c032b0c49b05dc4d2eaa747852b06.tar.gz |
Merge branch 'bjorn/erts/backport-zlib-fix/ERIERL-657/OTP-17470' into maint-22
* bjorn/erts/backport-zlib-fix/ERIERL-657/OTP-17470:
zlib: Fix edge case in state flushing
-rw-r--r-- | erts/emulator/nifs/common/zlib_nif.c | 8 | ||||
-rw-r--r-- | lib/kernel/test/zlib_SUITE.erl | 16 |
2 files changed, 22 insertions, 2 deletions
diff --git a/erts/emulator/nifs/common/zlib_nif.c b/erts/emulator/nifs/common/zlib_nif.c index b709ed5a6f..2710c586c6 100644 --- a/erts/emulator/nifs/common/zlib_nif.c +++ b/erts/emulator/nifs/common/zlib_nif.c @@ -370,6 +370,7 @@ static int zlib_flush_queue(int (*codec)(z_stream*, int), ErlNifEnv *env, d->s.next_in = input_vec[vec_idx].iov_base; d->s.avail_in = block_size; + /* We don't flush until reaching the end of our input. */ res = codec(&d->s, Z_NO_FLUSH); ASSERT(d->s.avail_in == 0 || d->s.avail_out == 0 || res != Z_OK); @@ -395,7 +396,12 @@ static int zlib_flush_queue(int (*codec)(z_stream*, int), ErlNifEnv *env, res = Z_BUF_ERROR; } - if(res == Z_OK && flush != Z_NO_FLUSH && (*bytes_remaining == 0)) { + if(res == Z_OK && (*bytes_remaining == 0) && d->s.avail_out > 0) { + /* We've reached the end of our input and need to flush the zlib state. + * + * Note that we do this even when the flush parameter is Z_NO_FLUSH as + * we may have filled our output buffer on the previous call. It will + * nop when there's nothing left to flush. */ d->s.next_in = NULL; d->s.avail_in = 0; diff --git a/lib/kernel/test/zlib_SUITE.erl b/lib/kernel/test/zlib_SUITE.erl index 52ae1b3ae6..215c91ef76 100644 --- a/lib/kernel/test/zlib_SUITE.erl +++ b/lib/kernel/test/zlib_SUITE.erl @@ -395,6 +395,7 @@ api_inflateReset(Config) when is_list(Config) -> api_inflate2(Config) when is_list(Config) -> Data = [<<1,2,2,3,3,3,4,4,4,4>>], Compressed = zlib:compress(Data), + Z1 = zlib:open(), ?m(ok, zlib:inflateInit(Z1)), ?m([], zlib:inflate(Z1, <<>>)), @@ -408,7 +409,20 @@ api_inflate2(Config) when is_list(Config) -> ?m(ok, zlib:inflateEnd(Z1)), ?m(ok, zlib:inflateInit(Z1)), ?m(?EXIT(data_error), zlib:inflate(Z1, <<2,1,2,1,2>>)), - ?m(ok, zlib:close(Z1)). + ?m(ok, zlib:close(Z1)), + + %% OTP-17299: we failed to fully flush the zlib state if we ran out of + %% input and filled the internal output buffer at the same time. + EdgeCaseData = <<"gurka", 0:16384/integer-unit:8>>, + EdgeCaseZipped = zlib:zip(EdgeCaseData), + Z2 = zlib:open(), + ?m(ok, zlib:inflateInit(Z2, -15)), + Unzipped = iolist_to_binary(zlib:inflate(Z2, EdgeCaseZipped)), + ?m(EdgeCaseData, Unzipped), + ?m(ok, zlib:inflateEnd(Z2)), + ?m(ok, zlib:close(Z2)), + + ok. %% Test inflate/3; same as inflate/2 but with the default options inverted. api_inflate3(Config) when is_list(Config) -> |