changeset 22055:4ae88bb10a94

lib: Fix crash when seeking istream-concat to EOF and trying to read it
author Timo Sirainen <timo.sirainen@dovecot.fi>
date Thu, 18 May 2017 22:18:15 +0300
parents 412e15c438bc
children 81a4cf22408f
files src/lib/istream-concat.c src/lib/test-istream-concat.c
diffstat 2 files changed, 30 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib/istream-concat.c	Thu May 18 21:10:33 2017 +0300
+++ b/src/lib/istream-concat.c	Thu May 18 22:18:15 2017 +0300
@@ -10,6 +10,7 @@
 
 	struct istream **input, *cur_input;
 	uoff_t *input_size;
+	unsigned int input_count;
 
 	unsigned int cur_idx, unknown_size_idx;
 	size_t prev_stream_left, prev_stream_skip, prev_skip;
@@ -29,7 +30,7 @@
 	}
 
 	if (close_parent) {
-		for (i = 0; cstream->input[i] != NULL; i++)
+		for (i = 0; i < cstream->input_count; i++)
 			i_stream_close(cstream->input[i]);
 	}
 }
@@ -39,7 +40,7 @@
 	struct concat_istream *cstream = (struct concat_istream *)stream;
 	unsigned int i;
 
-	for (i = 0; cstream->input[i] != NULL; i++)
+	for (i = 0; i < cstream->input_count; i++)
 		i_stream_unref(&cstream->input[i]);
 	i_free(cstream->input);
 	i_free(cstream->input_size);
@@ -54,7 +55,7 @@
 	unsigned int i;
 
 	cstream->istream.max_buffer_size = max_size;
-	for (i = 0; cstream->input[i] != NULL; i++)
+	for (i = 0; i < cstream->input_count; i++)
 		i_stream_set_max_buffer_size(cstream->input[i], max_size);
 }
 
@@ -167,7 +168,7 @@
 		}
 
 		/* we either read something or we're at EOF */
-		last_stream = cstream->input[cstream->cur_idx+1] == NULL;
+		last_stream = cstream->cur_idx+1 >= cstream->input_count;
 		if (ret == -1 && !last_stream) {
 			if (stream->pos - stream->skip >= i_stream_get_max_buffer_size(&stream->istream))
 				return -2;
@@ -228,7 +229,7 @@
 	const struct stat *st;
 	unsigned int i;
 
-	for (i = 0; cstream->input[i] != NULL; i++) {
+	for (i = 0; i < cstream->input_count; i++) {
 		if (*v_offset == 0) {
 			/* seek to beginning of this stream */
 			break;
@@ -277,8 +278,9 @@
 		stream->istream.stream_errno = EINVAL;
 		return;
 	}
-	cstream->cur_input = cstream->input[cstream->cur_idx];
-	if (cstream->cur_input == NULL) {
+	if (cstream->cur_idx < cstream->input_count)
+		cstream->cur_input = cstream->input[cstream->cur_idx];
+	else {
 		/* we allow seeking to EOF, but not past it. */
 		if (v_offset != 0) {
 			io_stream_set_error(&cstream->istream.iostream,
@@ -333,10 +335,10 @@
 	i_assert(count != 0);
 
 	cstream = i_new(struct concat_istream, 1);
-	cstream->input = i_new(struct istream *, count + 1);
-	cstream->input_size = i_new(uoff_t, count + 1);
+	cstream->input_count = count;
+	cstream->input = p_memdup(default_pool, input, sizeof(*input) * count);
+	cstream->input_size = i_new(uoff_t, count);
 
-	memcpy(cstream->input, input, sizeof(*input) * count);
 	cstream->cur_input = cstream->input[0];
 	i_stream_seek(cstream->cur_input, 0);
 
--- a/src/lib/test-istream-concat.c	Thu May 18 21:10:33 2017 +0300
+++ b/src/lib/test-istream-concat.c	Thu May 18 22:18:15 2017 +0300
@@ -117,6 +117,23 @@
 	return !test_has_failed();
 }
 
+static void test_istream_concat_seek_end(void)
+{
+	test_begin("istream concat seek end");
+
+	struct istream *streams[] = {
+		test_istream_create("s1"),
+		test_istream_create("s2"),
+		NULL
+	};
+	struct istream *input = i_stream_create_concat(streams);
+	i_stream_seek(input, 4);
+	test_assert(i_stream_read(input) == -1);
+	i_stream_unref(&input);
+
+	test_end();
+}
+
 static void test_istream_concat_early_end(void)
 {
 	struct istream *input, *streams[2];
@@ -158,5 +175,6 @@
 	} T_END;
 	test_end();
 
+	test_istream_concat_seek_end();
 	test_istream_concat_early_end();
 }