From: Timo Sirainen Date: Wed, 27 Jan 2021 18:29:11 +0000 (+0200) Subject: lib-compression: Fix potential hangs with non-blocking istreams X-Git-Tag: 2.3.15~143 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=9a1eba3f4164581fcca9a5d27260261add7edbc7;p=thirdparty%2Fdovecot%2Fcore.git lib-compression: Fix potential hangs with non-blocking istreams --- diff --git a/src/lib-compression/istream-lz4.c b/src/lib-compression/istream-lz4.c index 788c1cc8bf..8638979a6e 100644 --- a/src/lib-compression/istream-lz4.c +++ b/src/lib-compression/istream-lz4.c @@ -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. */ diff --git a/src/lib-compression/istream-lzma.c b/src/lib-compression/istream-lzma.c index 126b7aa12c..7b0c2a6a84 100644 --- a/src/lib-compression/istream-lzma.c +++ b/src/lib-compression/istream-lzma.c @@ -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; diff --git a/src/lib-compression/istream-zlib.c b/src/lib-compression/istream-zlib.c index 777289eb29..a3105a2bde 100644 --- a/src/lib-compression/istream-zlib.c +++ b/src/lib-compression/istream-zlib.c @@ -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: diff --git a/src/lib-compression/istream-zstd.c b/src/lib-compression/istream-zstd.c index 57fb796c8b..9c9ca4f865 100644 --- a/src/lib-compression/istream-zstd.c +++ b/src/lib-compression/istream-zstd.c @@ -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;