]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-compression: istream-lz4 - Refactor how chunk header is read
authorAki Tuomi <aki.tuomi@open-xchange.com>
Wed, 26 Feb 2020 10:57:06 +0000 (12:57 +0200)
committeraki.tuomi <aki.tuomi@open-xchange.com>
Fri, 6 Mar 2020 09:00:50 +0000 (09:00 +0000)
Chunk headers can sometime be at read borders. In this case more
data needs to be read. Fixes

Panic: file istream.c: line 333 (i_stream_read_memarea): assertion failed: (!stream->blocking)

src/lib-compression/istream-lz4.c

index b7100ec6e6f31da3445bb559210ff392c64d33a0..53f7947679ec11a9fd6bef4b0f10f3a20c3cf990 100644 (file)
@@ -85,6 +85,46 @@ static int i_stream_lz4_read_header(struct lz4_istream *zstream)
        return 1;
 }
 
+static int i_stream_lz4_read_chunk_header(struct lz4_istream *zstream)
+{
+       int ret;
+       size_t size;
+       const unsigned char *data;
+       struct istream_private *stream = &zstream->istream;
+
+       i_assert(zstream->chunk_buf->used <= IOSTREAM_LZ4_CHUNK_PREFIX_LEN);
+       ret = i_stream_read_more(stream->parent, &data, &size);
+       size = I_MIN(size, IOSTREAM_LZ4_CHUNK_PREFIX_LEN - zstream->chunk_buf->used);
+       buffer_append(zstream->chunk_buf, data, size);
+       i_stream_skip(stream->parent, size);
+       if (ret < 0) {
+               stream->istream.stream_errno = stream->parent->stream_errno;
+               if (stream->istream.stream_errno == 0) {
+                       stream->istream.eof = TRUE;
+                       stream->cached_stream_size =
+                               stream->istream.v_offset +
+                               stream->pos - stream->skip;
+               }
+               return ret;
+       }
+       i_assert(ret != 0 || !stream->istream.blocking);
+       if (ret == 0)
+               return ret;
+       if (zstream->chunk_buf->used < IOSTREAM_LZ4_CHUNK_PREFIX_LEN)
+               return 0;
+       zstream->chunk_size = zstream->chunk_left =
+               be32_to_cpu_unaligned(zstream->chunk_buf->data);
+       if (zstream->chunk_size == 0 ||
+           zstream->chunk_size > ISTREAM_LZ4_CHUNK_SIZE) {
+               lz4_read_error(zstream, t_strdup_printf(
+                       "invalid lz4 chunk size: %u", zstream->chunk_size));
+               stream->istream.stream_errno = EINVAL;
+               return -1;
+       }
+       buffer_set_used_size(zstream->chunk_buf, 0);
+       return 1;
+}
+
 static ssize_t i_stream_lz4_read(struct istream_private *stream)
 {
        struct lz4_istream *zstream = (struct lz4_istream *)stream;
@@ -105,38 +145,12 @@ static ssize_t i_stream_lz4_read(struct istream_private *stream)
        }
 
        if (zstream->chunk_left == 0) {
-               i_assert(zstream->chunk_buf->used <= IOSTREAM_LZ4_CHUNK_PREFIX_LEN);
-               ret = i_stream_read_more(stream->parent, &data, &size);
-               if (size > IOSTREAM_LZ4_CHUNK_PREFIX_LEN - zstream->chunk_buf->used)
-                       size = IOSTREAM_LZ4_CHUNK_PREFIX_LEN - zstream->chunk_buf->used;
-               buffer_append(zstream->chunk_buf, data, size);
-               i_stream_skip(stream->parent, size);
-               if (ret < 0) {
-                       stream->istream.stream_errno =
-                               stream->parent->stream_errno;
-                       if (stream->istream.stream_errno == 0) {
-                               stream->istream.eof = TRUE;
-                               stream->cached_stream_size =
-                                       stream->istream.v_offset +
-                                       stream->pos - stream->skip;
-                       }
-                       return ret;
+               while ((ret = i_stream_lz4_read_chunk_header(zstream)) == 0) {
+                       if (ret == 0 && !stream->istream.blocking)
+                               return 0;
                }
-               if (ret == 0 && !stream->istream.eof)
-                       return 0;
-               if (zstream->chunk_buf->used < IOSTREAM_LZ4_CHUNK_PREFIX_LEN)
-                       return 0;
-               zstream->chunk_size = zstream->chunk_left =
-                       be32_to_cpu_unaligned(zstream->chunk_buf->data);
-               buffer_set_used_size(zstream->chunk_buf, 0);
-               if (zstream->chunk_size == 0 ||
-                   zstream->chunk_size > ISTREAM_LZ4_CHUNK_SIZE) {
-                       lz4_read_error(zstream, t_strdup_printf(
-                               "invalid lz4 chunk size: %u", zstream->chunk_size));
-                       stream->istream.stream_errno = EINVAL;
-                       return -1;
-               }
-               buffer_set_used_size(zstream->chunk_buf, 0);
+               if (ret < 0)
+                       return ret;
        }
 
        /* read the whole compressed chunk into memory */
@@ -156,6 +170,7 @@ static ssize_t i_stream_lz4_read(struct istream_private *stream)
                }
                zstream->istream.istream.stream_errno =
                        zstream->istream.parent->stream_errno;
+               i_assert(ret != 0 || !stream->istream.blocking);
                return ret;
        }
        /* if we already have max_buffer_size amount of data, fail here */