]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-compression: Fix potential hangs writing to non-blocking ostreams
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Tue, 3 Jun 2025 15:11:09 +0000 (18:11 +0300)
committertimo.sirainen <timo.sirainen@open-xchange.com>
Sat, 12 Jul 2025 06:49:23 +0000 (06:49 +0000)
This especially fixes hangs with IMAP COMPRESS handling in proxies.

src/lib-compression/ostream-bzlib.c
src/lib-compression/ostream-lz4.c
src/lib-compression/ostream-zlib.c
src/lib-compression/ostream-zstd.c

index 839fd22138995f6b457b7a20c62086546ec0ac36..c7e228b05a44c9c5c1543d428c7922854dfde26c 100644 (file)
@@ -94,6 +94,11 @@ static int o_stream_zlib_send_outbuf(struct bzlib_ostream *zstream)
        }
        if ((size_t)ret != size) {
                zstream->outbuf_offset += ret;
+               /* We couldn't send everything to parent stream, but we
+                  accepted all the input already. Set the ostream's flush
+                  pending so when there's more space in the parent stream
+                  we'll continue sending the rest of the data. */
+               o_stream_set_flush_pending(&zstream->ostream.ostream, TRUE);
                return 0;
        }
        zstream->outbuf_offset = 0;
@@ -301,6 +306,7 @@ static struct ostream *o_stream_create_bz2(struct ostream *output, int level)
 
        zstream->zs.next_out = zstream->outbuf;
        zstream->zs.avail_out = sizeof(zstream->outbuf);
+       o_stream_init_buffering_flush(&zstream->ostream, output);
        return o_stream_create(&zstream->ostream, output,
                               o_stream_get_fd(output));
 }
index 65961603aeb6d3f0606d603f4541a86b01a6b8d1..0707e12d43dbdc2299c94fe1dff5f3b5082900a6 100644 (file)
@@ -50,6 +50,11 @@ static int o_stream_lz4_send_outbuf(struct lz4_ostream *zstream)
        }
        if ((size_t)ret != size) {
                zstream->outbuf_offset += ret;
+               /* We couldn't send everything to parent stream, but we
+                  accepted all the input already. Set the ostream's flush
+                  pending so when there's more space in the parent stream
+                  we'll continue sending the rest of the data. */
+               o_stream_set_flush_pending(&zstream->ostream.ostream, TRUE);
                return 0;
        }
        zstream->outbuf_offset = 0;
@@ -226,6 +231,7 @@ o_stream_create_lz4_auto(struct ostream *output,
        hdr->max_uncompressed_chunk_size[3] =
                (OSTREAM_LZ4_CHUNK_SIZE & 0x000000ff);
        zstream->outbuf_used = sizeof(*hdr);
+       o_stream_init_buffering_flush(&zstream->ostream, output);
        return o_stream_create(&zstream->ostream, output,
                               o_stream_get_fd(output));
 }
index 1367713e68ad1c1c993d5e6bd7bd5c480176d63c..5cc22157376f01f5b5410d2b53b95c212366abe7 100644 (file)
@@ -109,7 +109,11 @@ static int o_stream_zlib_send_gz_header(struct zlib_ostream *zstream)
        }
        i_assert((size_t)ret <= zstream->header_bytes_left);
        zstream->header_bytes_left -= ret;
-       return zstream->header_bytes_left == 0 ? 1 : 0;
+       if (zstream->header_bytes_left != 0) {
+               o_stream_set_flush_pending(&zstream->ostream.ostream, TRUE);
+               return 0;
+       }
+       return 1;
 }
 
 static int o_stream_zlib_lsb_uint32(struct ostream *output, uint32_t num)
@@ -159,6 +163,11 @@ static int o_stream_zlib_send_outbuf(struct zlib_ostream *zstream)
        }
        if ((size_t)ret != size) {
                zstream->outbuf_offset += ret;
+               /* We couldn't send everything to parent stream, but we
+                  accepted all the input already. Set the ostream's flush
+                  pending so when there's more space in the parent stream
+                  we'll continue sending the rest of the data. */
+               o_stream_set_flush_pending(&zstream->ostream.ostream, TRUE);
                return 0;
        }
        zstream->outbuf_offset = 0;
@@ -414,6 +423,7 @@ o_stream_create_zlib(struct ostream *output, int level, bool gz)
 
        zstream->zs.next_out = zstream->outbuf;
        zstream->zs.avail_out = sizeof(zstream->outbuf);
+       o_stream_init_buffering_flush(&zstream->ostream, output);
        return o_stream_create(&zstream->ostream, output,
                               o_stream_get_fd(output));
 }
index 786765f1376a43bbf3d70ee7a2c364f8dd4cf8a3..b307fefd603583557351bcac9bc9323d9a3a374e 100644 (file)
@@ -115,8 +115,14 @@ static ssize_t o_stream_zstd_send_outbuf(struct zstd_ostream *zstream)
                memmove(zstream->outbuf, zstream->outbuf+ret, zstream->output.pos-ret);
                zstream->output.pos -= ret;
        }
-       if (zstream->output.pos > 0)
+       if (zstream->output.pos > 0) {
+               /* We couldn't send everything to parent stream, but we
+                  accepted all the input already. Set the ostream's flush
+                  pending so when there's more space in the parent stream
+                  we'll continue sending the rest of the data. */
+               o_stream_set_flush_pending(&zstream->ostream.ostream, TRUE);
                return 0;
+       }
        return 1;
 }
 
@@ -263,6 +269,7 @@ o_stream_create_zstd(struct ostream *output, int level)
                zstream->output.dst = zstream->outbuf;
                zstream->output.size = ZSTD_CStreamOutSize();
        }
+       o_stream_init_buffering_flush(&zstream->ostream, output);
        return o_stream_create(&zstream->ostream, output,
                               o_stream_get_fd(output));
 }