]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib: seq-range-array - Assert-crash if array becomes 0..(uint32_t)-1
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Thu, 1 Oct 2020 08:37:24 +0000 (11:37 +0300)
committeraki.tuomi <aki.tuomi@open-xchange.com>
Thu, 22 Oct 2020 10:23:02 +0000 (10:23 +0000)
src/lib/seq-range-array.c
src/lib/seq-range-array.h
src/lib/test-lib.inc
src/lib/test-seq-range-array.c

index e320b948d88c7cb97222e07a1cb27321709145dd..2d8dbfcb1a9ca147ffe57be8c181a3025997ac14 100644 (file)
@@ -4,6 +4,16 @@
 #include "array.h"
 #include "seq-range-array.h"
 
+static bool seq_range_is_overflowed(const ARRAY_TYPE(seq_range) *array)
+{
+       const struct seq_range *range;
+       unsigned int count;
+
+       range = array_get(array, &count);
+       return count == 1 && range[0].seq1 == 0 &&
+               range[0].seq2 == (uint32_t)-1;
+}
+
 static bool ATTR_NOWARN_UNUSED_RESULT
 seq_range_lookup(const ARRAY_TYPE(seq_range) *array,
                 uint32_t seq, unsigned int *idx_r)
@@ -107,6 +117,7 @@ bool seq_range_array_add(ARRAY_TYPE(seq_range) *array, uint32_t seq)
        } else {
                exists = seq_range_array_add_slow_path(array, seq);
        }
+       i_assert(!seq_range_is_overflowed(array));
        return exists;
 }
 
@@ -194,6 +205,7 @@ seq_range_array_add_range_internal(ARRAY_TYPE(seq_range) *array,
                        array_delete(array, idx1 + 1, idx2 - idx1);
                }
        }
+       i_assert(!seq_range_is_overflowed(array));
 }
 
 void seq_range_array_add_range(ARRAY_TYPE(seq_range) *array,
@@ -353,6 +365,7 @@ unsigned int seq_range_array_remove_range(ARRAY_TYPE(seq_range) *array,
        for (idx2 = idx; idx2 < count; idx2++) {
                if (data[idx2].seq1 > seq2)
                        break;
+               i_assert(UINT_MAX - remove_count >= seq_range_length(&data[idx2]));
                remove_count += seq_range_length(&data[idx2]);
        }
        array_delete(array, idx, idx2-idx);
@@ -362,14 +375,16 @@ unsigned int seq_range_array_remove_range(ARRAY_TYPE(seq_range) *array,
 unsigned int seq_range_array_remove_seq_range(ARRAY_TYPE(seq_range) *dest,
                                              const ARRAY_TYPE(seq_range) *src)
 {
-       unsigned int ret = 0;
+       unsigned int count, full_count = 0;
        const struct seq_range *src_range;
 
        array_foreach(src, src_range) {
-               ret += seq_range_array_remove_range(dest, src_range->seq1,
-                                                   src_range->seq2);
+               count = seq_range_array_remove_range(dest, src_range->seq1,
+                                                    src_range->seq2);
+               i_assert(UINT_MAX - full_count >= count);
+               full_count += count;
        }
-       return ret;
+       return full_count;
 }
 
 void seq_range_array_remove_nth(ARRAY_TYPE(seq_range) *array,
@@ -398,22 +413,26 @@ unsigned int seq_range_array_intersect(ARRAY_TYPE(seq_range) *dest,
                                       const ARRAY_TYPE(seq_range) *src)
 {
        const struct seq_range *src_range;
-       unsigned int i, count, ret = 0;
+       unsigned int i, count, remove_count, full_count = 0;
        uint32_t last_seq = 0;
 
        src_range = array_get(src, &count);
        for (i = 0; i < count; i++) {
                if (last_seq + 1 < src_range[i].seq1) {
-                       ret += seq_range_array_remove_range(dest,
-                                       last_seq + 1, src_range[i].seq1 - 1);
+                       remove_count = seq_range_array_remove_range(dest,
+                               last_seq + 1, src_range[i].seq1 - 1);
+                       i_assert(UINT_MAX - full_count >= remove_count);
+                       full_count += remove_count;
                }
                last_seq = src_range[i].seq2;
        }
        if (last_seq != (uint32_t)-1) {
-               ret += seq_range_array_remove_range(dest, last_seq + 1,
-                                                   (uint32_t)-1);
+               remove_count = seq_range_array_remove_range(dest, last_seq + 1,
+                                                           (uint32_t)-1);
+               i_assert(UINT_MAX - full_count >= remove_count);
+               full_count += remove_count;
        }
-       return ret;
+       return full_count;
 }
 
 bool seq_range_exists(const ARRAY_TYPE(seq_range) *array, uint32_t seq)
@@ -449,8 +468,10 @@ unsigned int seq_range_count(const ARRAY_TYPE(seq_range) *array)
        const struct seq_range *range;
        unsigned int seq_count = 0;
 
-       array_foreach(array, range)
+       array_foreach(array, range) {
+               i_assert(UINT_MAX - seq_count >= seq_range_length(range));
                seq_count += seq_range_length(range);
+       }
        return seq_count;
 }
 
index 3d0dfbd54c6f138ee2d06faa297804e0f6fce4c2..e323eaddc498f5be172928ead7c7d3598a95092b 100644 (file)
@@ -1,6 +1,11 @@
 #ifndef SEQ_RANGE_ARRAY_H
 #define SEQ_RANGE_ARRAY_H
 
+/* NOTE: A full 0..UINT_MAX sequence range isn't valid to use here, because its
+   size would become UINT_MAX+1, which can't be returned by e.g.
+   seq_range_count() and similar functions. Attempting to use such sequence
+   ranges will result in assert-crash. */
+
 struct seq_range {
        uint32_t seq1, seq2;
 };
@@ -14,6 +19,7 @@ struct seq_range_iter {
 static inline uint32_t ATTR_PURE seq_range_length(const struct seq_range *range)
 {
        i_assert(range->seq2 >= range->seq1);
+       i_assert(range->seq1 > 0 || range->seq2 < (uint32_t)-1);
        return range->seq2 - range->seq1 + 1;
 }
 
index 9f75b24fd3521efe8215468cdb9c266aa0efea00..ec03b22c793f6af2b1b745032b1c5e6e0b1e3540 100644 (file)
@@ -83,6 +83,7 @@ TEST(test_priorityq)
 TEST(test_random)
 FATAL(fatal_random)
 TEST(test_seq_range_array)
+FATAL(fatal_seq_range_array)
 TEST(test_stats_dist)
 TEST(test_str)
 TEST(test_strescape)
index f187305c2f4756221bf44796efe0d7b49c535bc6..ca0178a60c4717b46876b41abc80f4196a542a70 100644 (file)
@@ -124,14 +124,13 @@ static void test_seq_range_array_remove_range(void)
        test_begin("seq_range_array_remove_range()");
        t_array_init(&range, 8);
 
-       seq_range_array_add_range(&range, 0, (uint32_t)-1);
+       seq_range_array_add_range(&range, 0, (uint32_t)-2);
        test_assert(seq_range_array_remove_range(&range, 0, 2) == 3);
-       r = array_front(&range); test_assert(r->seq1 == 3 && r->seq2 == (uint32_t)-1);
+       r = array_front(&range); test_assert(r->seq1 == 3 && r->seq2 == (uint32_t)-2);
 
-       seq_range_array_add_range(&range, 0, (uint32_t)-1);
+       seq_range_array_add_range(&range, 0, (uint32_t)-2);
        test_assert(array_count(&range) == 1);
-       /* return value wraps to 0 because it doesn't fit into uint32_t */
-       test_assert(seq_range_array_remove_range(&range, 0, (uint32_t)-1) == 0);
+       test_assert(seq_range_array_remove_range(&range, 0, (uint32_t)-2) == UINT_MAX);
        test_assert(array_count(&range) == 0);
 
        seq_range_array_add_range(&range, (uint32_t)-1, (uint32_t)-1);
@@ -287,8 +286,8 @@ static void test_seq_range_array_invert_edges(void)
        } tests[] = {
                { -1, -1, -1, -1,
                  0, 0xffffffff, -1, -1 },
-               { 0, 0xffffffff, -1, -1,
-                 -1, -1, -1, -1 },
+               /*{ 0, 0xffffffff, -1, -1, too large, will assert-crash
+                 -1, -1, -1, -1 }, */
                { 0, 0xfffffffe, -1, -1,
                  0xffffffff, 0xffffffff, -1, -1 },
                { 1, 0xfffffffe, -1, -1,
@@ -374,3 +373,47 @@ void test_seq_range_array(void)
        test_seq_range_array_have_common();
        test_seq_range_array_random();
 }
+
+enum fatal_test_state fatal_seq_range_array(unsigned int stage)
+{
+       ARRAY_TYPE(seq_range) arr;
+       struct seq_range *range;
+
+       t_array_init(&arr, 2);
+       switch (stage) {
+       case 0:
+               test_begin("seq_range_array fatals");
+               test_expect_fatal_string("!seq_range_is_overflowed(array)");
+               seq_range_array_add_range(&arr, 0, (uint32_t)-1);
+               return FATAL_TEST_FAILURE;
+       case 1:
+               seq_range_array_add_range(&arr, 1, (uint32_t)-1);
+               test_expect_fatal_string("!seq_range_is_overflowed(array)");
+               seq_range_array_add(&arr, 0);
+               return FATAL_TEST_FAILURE;
+       case 2:
+               seq_range_array_add_range(&arr, 0, (uint32_t)-2);
+               test_expect_fatal_string("!seq_range_is_overflowed(array)");
+               seq_range_array_add(&arr, (uint32_t)-1);
+               return FATAL_TEST_FAILURE;
+       case 3:
+               range = array_append_space(&arr);
+               range->seq2 = (uint32_t)-1;
+               test_expect_fatal_string("range->seq1 > 0 || range->seq2 < (uint32_t)-1");
+               i_error("This shouldn't return: %u", seq_range_count(&arr));
+               return FATAL_TEST_FAILURE;
+       case 4:
+               range = array_append_space(&arr);
+               range->seq2 = (uint32_t)-2;
+               test_assert(seq_range_count(&arr) == (uint32_t)-1);
+
+               range = array_append_space(&arr);
+               range->seq1 = (uint32_t)-2;
+               range->seq2 = (uint32_t)-1;
+               test_expect_fatal_string("UINT_MAX - seq_count >= seq_range_length(range)");
+               i_error("This shouldn't return: %u", seq_range_count(&arr));
+               return FATAL_TEST_FAILURE;
+       }
+       test_end();
+       return FATAL_TEST_FINISHED;
+}