]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib: Fix seq_range_array_invert() when input contains 2^32-1
authorTimo Sirainen <timo.sirainen@dovecot.fi>
Thu, 8 Dec 2016 15:50:46 +0000 (17:50 +0200)
committerGitLab <gitlab@git.dovecot.net>
Thu, 8 Dec 2016 17:52:05 +0000 (19:52 +0200)
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)

src/lib/seq-range-array.c
src/lib/test-seq-range-array.c

index c29555adbc99a81f18c0462a843fcd3d1d77da0a..0d45735fbb2a91557330846adec23456e194adbe 100644 (file)
@@ -427,7 +427,7 @@ void seq_range_array_invert(ARRAY_TYPE(seq_range) *array,
 {
        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 @@ void seq_range_array_invert(ARRAY_TYPE(seq_range) *array,
        }
 
        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;
index 9ac890675e8721794435175a2173f9026b776392..07bae25d93cf49128b77ba6e17c0d4cda4e4257e 100644 (file)
@@ -170,6 +170,7 @@ static void test_seq_range_array_invert(void)
        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(void)
        }
 }
 
+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 @@ void test_seq_range_array(void)
        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();
 }