changeset 21527:09c06f63c8c4

lib-compress: Fix assert-crash when .gz header size exceeds buffer max length Instead treat the stream as corrupted and return EINVAL. Fixes: Panic: file istream.c: line 182 (i_stream_read): assertion failed: (_stream->skip != _stream->pos)
author Timo Sirainen <timo.sirainen@dovecot.fi>
date Mon, 13 Feb 2017 18:05:13 +0200
parents 6109df9e091b
children a9b6b316b9a5
files src/lib-compression/istream-zlib.c src/lib-compression/test-compression.c
diffstat 2 files changed, 46 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-compression/istream-zlib.c	Fri Feb 10 15:27:13 2017 +0200
+++ b/src/lib-compression/istream-zlib.c	Mon Feb 13 18:05:13 2017 +0200
@@ -81,6 +81,11 @@
 			zlib_read_error(zstream, "missing gz trailer");
 			stream->istream.stream_errno = EINVAL;
 		}
+		if (ret == -2) {
+			zlib_read_error(zstream, "gz header is too large");
+			stream->istream.stream_errno = EINVAL;
+			ret = -1;
+		}
 		return ret;
 	}
 	zstream->prev_size = size;
--- a/src/lib-compression/test-compression.c	Fri Feb 10 15:27:13 2017 +0200
+++ b/src/lib-compression/test-compression.c	Mon Feb 13 18:05:13 2017 +0200
@@ -7,6 +7,7 @@
 #include "sha1.h"
 #include "randgen.h"
 #include "test-common.h"
+#include "istream-zlib.h"
 #include "compression.h"
 
 #include <unistd.h>
@@ -159,6 +160,45 @@
 	test_end();
 }
 
+static void test_gz_large_header(void)
+{
+	static const unsigned char gz_input[] = {
+		0x1f, 0x8b, 0x08, 0x08,
+		'a','a','a','a','a','a','a','a','a','a','a',
+		0
+	};
+	struct istream *file_input, *input;
+	size_t i;
+
+	test_begin("gz large header");
+
+	/* max buffer size smaller than gz header */
+	for (i = 1; i < sizeof(gz_input); i++) {
+		file_input = test_istream_create_data(gz_input, sizeof(gz_input));
+		test_istream_set_size(file_input, i);
+		test_istream_set_max_buffer_size(file_input, i);
+
+		input = i_stream_create_gz(file_input, FALSE);
+		test_assert_idx(i_stream_read(input) == 0, i);
+		test_assert_idx(i_stream_read(input) == -1 &&
+				input->stream_errno == EINVAL, i);
+		i_stream_unref(&input);
+		i_stream_unref(&file_input);
+	}
+
+	/* max buffer size is exactly the gz header */
+	file_input = test_istream_create_data(gz_input, sizeof(gz_input));
+	input = i_stream_create_gz(file_input, FALSE);
+	test_istream_set_size(input, i);
+	test_istream_set_allow_eof(input, FALSE);
+	test_istream_set_max_buffer_size(input, i);
+	test_assert(i_stream_read(input) == 0);
+	i_stream_unref(&input);
+	i_stream_unref(&file_input);
+
+	test_end();
+}
+
 static void test_uncompress_file(const char *path)
 {
 	const struct compression_handler *handler;
@@ -249,6 +289,7 @@
 		test_compression,
 		test_gz_concat,
 		test_gz_no_concat,
+		test_gz_large_header,
 		NULL
 	};
 	if (argc == 2) {