File 0278-zlib-Fix-edge-case-in-state-flushing.patch of Package erlang

From 6708bbded2235dfe4478b0cd7fd5e277fc57b344 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?John=20H=C3=B6gberg?= <john@erlang.org>
Date: Tue, 6 Apr 2021 10:14:16 +0200
Subject: [PATCH] zlib: Fix edge case in state flushing

---
 erts/emulator/nifs/common/zlib_nif.c |  8 +++++++-
 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) ->
-- 
2.26.2

openSUSE Build Service is sponsored by