]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-compression: Fix potential hangs with non-blocking istreams
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Wed, 27 Jan 2021 18:29:11 +0000 (20:29 +0200)
committeraki.tuomi <aki.tuomi@open-xchange.com>
Tue, 23 Mar 2021 10:13:23 +0000 (10:13 +0000)
src/lib-compression/istream-lz4.c
src/lib-compression/istream-lzma.c
src/lib-compression/istream-zlib.c
src/lib-compression/istream-zstd.c

index 788c1cc8bfcafd22a925a32b5bd32fcc6a05d735..8638979a6e89ff899d7635a40b31faaa7f978bd9 100644 (file)
@@ -177,6 +177,11 @@ static ssize_t i_stream_lz4_read(struct istream_private *stream)
        /* if we already have max_buffer_size amount of data, fail here */
        if (stream->pos - stream->skip >= i_stream_get_max_buffer_size(&stream->istream))
                return -2;
+       if (i_stream_get_data_size(zstream->istream.parent) > 0) {
+               /* Parent stream was only partially consumed. Set the stream's
+                  IO as pending to avoid hangs. */
+               i_stream_set_input_pending(&zstream->istream.istream, TRUE);
+       }
        /* allocate enough space for the old data and the new
           decompressed chunk. we don't know the original compressed size,
           so just allocate the max amount of memory. */
index 126b7aa12cfa3f182d6b9c0e8ff03acd93e5d315..7b0c2a6a8470352cf9df9996a32453fbe384115d 100644 (file)
@@ -147,7 +147,14 @@ static ssize_t i_stream_lzma_read(struct istream_private *stream)
        out_size -= zstream->strm.avail_out;
        stream->pos += out_size;
 
-       i_stream_skip(stream->parent, size - zstream->strm.avail_in);
+       size_t bytes_consumed = size - zstream->strm.avail_in;
+       i_stream_skip(stream->parent, bytes_consumed);
+       if (i_stream_get_data_size(stream->parent) > 0 &&
+           (bytes_consumed > 0 || out_size > 0)) {
+               /* Parent stream was only partially consumed. Set the stream's
+                  IO as pending to avoid hangs. */
+               i_stream_set_input_pending(&stream->istream, TRUE);
+       }
 
        if (lzma_handle_error(zstream, ret) < 0) {
                return -1;
index 777289eb29ffc057eff8b99fef51bb10046e71ad..a3105a2bdee9d92d7556b8f082ee879037987033 100644 (file)
@@ -260,7 +260,14 @@ static ssize_t i_stream_zlib_read(struct istream_private *stream)
                                         out_size);
        stream->pos += out_size;
 
-       i_stream_skip(stream->parent, size - zstream->zs.avail_in);
+       size_t bytes_consumed = size - zstream->zs.avail_in;
+       i_stream_skip(stream->parent, bytes_consumed);
+       if (i_stream_get_data_size(stream->parent) > 0 &&
+           (bytes_consumed > 0 || out_size > 0)) {
+               /* Parent stream was only partially consumed. Set the stream's
+                  IO as pending to avoid hangs. */
+               i_stream_set_input_pending(&stream->istream, TRUE);
+       }
 
        switch (ret) {
        case Z_OK:
index 57fb796c8bea6862d349185878da49b54fbbc41e..9c9ca4f865b85058f1220add1282b83558483b8a 100644 (file)
@@ -157,6 +157,10 @@ static ssize_t i_stream_zstd_read(struct istream_private *stream)
                        if (ret == 0)
                                return 0;
                        buffer_append(zstream->frame_buffer, data, size);
+                       /* NOTE: All of the parent stream input is skipped
+                          over here. This is why there's no need to call
+                          i_stream_set_input_pending() here like with other
+                          compression istreams. */
                        i_stream_skip(stream->parent, size);
                        zstream->input.src = zstream->frame_buffer->data;
                        zstream->input.size = zstream->frame_buffer->used;