changeset 21273:ff92e16346c9

lib: Fix seq_range_array_invert() when input contains 2^32-1 This caused next_min_seq to be wrapped to 0, which was handled wrong later on. Fixes: Panic: file mail-index-map.c: line 549 (mail_index_map_lookup_seq_range): assertion failed: (first_uid > 0)
author Timo Sirainen <timo.sirainen@dovecot.fi>
date Thu, 08 Dec 2016 17:50:46 +0200
parents 1be2f1e78975
children 05e9415e545a
files src/lib/seq-range-array.c src/lib/test-seq-range-array.c
diffstat 2 files changed, 67 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib/seq-range-array.c	Wed Dec 07 00:14:08 2016 +0200
+++ b/src/lib/seq-range-array.c	Thu Dec 08 17:50:46 2016 +0200
@@ -427,7 +427,7 @@
 {
 	struct seq_range *range, value;
 	unsigned int i, count;
-	uint32_t next_min_seq;
+	uint32_t prev_min_seq;
 
 	if (array_is_created(array))
 		range = array_get_modifiable(array, &count);
@@ -454,16 +454,25 @@
 	}
 
 	for (i = 0; i < count; ) {
-		next_min_seq = range[i].seq2 + 1;
-		if (range[i].seq1 == min_seq) {
+		prev_min_seq = min_seq;
+		min_seq = range[i].seq2;
+		if (range[i].seq1 == prev_min_seq) {
 			array_delete(array, i, 1);
 			range = array_get_modifiable(array, &count);
 		} else {
 			range[i].seq2 = range[i].seq1 - 1;
-			range[i].seq1 = min_seq;
+			range[i].seq1 = prev_min_seq;
 			i++;
 		}
-		min_seq = next_min_seq;
+		if (min_seq >= max_seq) {
+			/* max_seq is reached. the rest of the array should be
+			   empty. we'll return here, because min_seq++ may
+			   wrap to 0. */
+			i_assert(min_seq == max_seq);
+			i_assert(i == count);
+			return;
+		}
+		min_seq++;
 	}
 	if (min_seq <= max_seq) {
 		value.seq1 = min_seq;
--- a/src/lib/test-seq-range-array.c	Wed Dec 07 00:14:08 2016 +0200
+++ b/src/lib/test-seq-range-array.c	Thu Dec 08 17:50:46 2016 +0200
@@ -170,6 +170,7 @@
 	static const unsigned int input[] = {
 		1, 2, 3, 4, 5, UINT_MAX,
 		2, 3, 4, UINT_MAX,
+		3, 4, 5, UINT_MAX,
 		1, 2, 4, 5, UINT_MAX,
 		1, 3, 5, UINT_MAX,
 		1, UINT_MAX,
@@ -207,6 +208,57 @@
 	}
 }
 
+static void test_seq_range_array_invert_edges(void)
+{
+	const struct {
+		int64_t a_seq1, a_seq2, b_seq1, b_seq2;
+		int64_t resa_seq1, resa_seq2, resb_seq1, resb_seq2;
+	} tests[] = {
+		{ -1, -1, -1, -1,
+		  0, 0xffffffff, -1, -1 },
+		{ 0, 0xffffffff, -1, -1,
+		  -1, -1, -1, -1 },
+		{ 0, 0xfffffffe, -1, -1,
+		  0xffffffff, 0xffffffff, -1, -1 },
+		{ 1, 0xfffffffe, -1, -1,
+		  0, 0, 0xffffffff, 0xffffffff },
+		{ 1, 0xffffffff, -1, -1,
+		  0, 0, -1, -1 },
+		{ 0, 0, 0xffffffff, 0xffffffff,
+		  1, 0xfffffffe, -1, -1 },
+		{ 0xffffffff, 0xffffffff, -1, -1,
+		  0, 0xfffffffe, -1, -1 },
+	};
+	ARRAY_TYPE(seq_range) range = ARRAY_INIT;
+	const struct seq_range *result;
+	unsigned int count;
+
+	test_begin("seq_range_array_invert() edges");
+	for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) T_BEGIN {
+		t_array_init(&range, 10);
+		if (tests[i].a_seq1 != -1)
+			seq_range_array_add_range(&range, tests[i].a_seq1, tests[i].a_seq2);
+		if (tests[i].b_seq1 != -1)
+			seq_range_array_add_range(&range, tests[i].b_seq1, tests[i].b_seq2);
+		seq_range_array_invert(&range, 0, 0xffffffff);
+
+		result = array_get(&range, &count);
+		if (tests[i].resa_seq1 == -1)
+			test_assert_idx(count == 0, i);
+		else {
+			test_assert(result[0].seq1 == tests[i].resa_seq1);
+			test_assert(result[0].seq2 == tests[i].resa_seq2);
+			if (tests[i].resb_seq1 == -1)
+				test_assert_idx(count == 1, i);
+			else {
+				test_assert(result[1].seq1 == tests[i].resb_seq1);
+				test_assert(result[1].seq2 == tests[i].resb_seq2);
+			}
+		}
+	} T_END;
+	test_end();
+}
+
 static void test_seq_range_create(ARRAY_TYPE(seq_range) *array, uint8_t byte)
 {
 	unsigned int i;
@@ -245,6 +297,7 @@
 	test_seq_range_array_add_merge();
 	test_seq_range_array_remove_nth();
 	test_seq_range_array_invert();
+	test_seq_range_array_invert_edges();
 	test_seq_range_array_have_common();
 	test_seq_range_array_random();
 }