]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
stats: Fix panic due to miscalculation of group-by buckets for non-multiple steps
authorFred Morcos <fred.morcos@open-xchange.com>
Wed, 4 Feb 2026 11:00:27 +0000 (12:00 +0100)
committeraki.tuomi <aki.tuomi@open-xchange.com>
Fri, 6 Feb 2026 11:12:45 +0000 (11:12 +0000)
Also add a linear group-by test with a non-multiple step.

Fixes: Panic: failed to find a matching bucket ...
src/stats/stats-settings.c
src/stats/test-stats-metrics.c

index f07d2510163f627e15eff10145b918c3021af1f2..456c579f2d21574c60de860aa62390bd353ac595 100644 (file)
@@ -358,7 +358,11 @@ void metrics_group_by_linear_init(struct stats_metric_settings_group_by *group_b
         * 'min + 1 * step + 1', the fourth at 'min + 2 * step + 1', and so on.
         */
        i_assert(step > 0);
-       group_by->num_ranges = (max - min) / step + 2;
+
+       /* num_ranges=ceil((max-min)/(double)step)+2 */
+       group_by->num_ranges = (((max - min) + step - 1) / step);
+       /* +2 is for the [-inf, min] bucket and the [max, inf] bucket. */
+       group_by->num_ranges += 2;
        group_by->ranges = p_new(pool, struct stats_metric_settings_bucket_range,
                                 group_by->num_ranges);
 
@@ -369,7 +373,7 @@ void metrics_group_by_linear_init(struct stats_metric_settings_group_by *group_b
        /* the [min, max] group-by buckets */
        for (unsigned int i = 1; i < group_by->num_ranges - 1; i++) {
                group_by->ranges[i].min = min + (i - 1) * step;
-               group_by->ranges[i].max = min + i * step;
+               group_by->ranges[i].max = I_MIN(min + i * step, max);
        }
 
        /* set up the [max, inf] group-by bucket */
index 6702ea6597413e1fe2fd3e182eba65dbff07d19f..6f00741215572870aaf5689a4646804e0425e4c6 100644 (file)
@@ -341,6 +341,48 @@ static const struct quantized_test quantized_tests[] = {
                  { { 10000, INTMAX_MAX }, 3 },
                }
        },
+       {
+               (const char *const []){
+                 "metric/test/group_by/foobar/method=linear",
+                 "metric/test/group_by/foobar/method/linear/method=linear",
+                 "metric/test/group_by/foobar/method/linear/min=500",
+                 "metric/test/group_by/foobar/method/linear/max=10000",
+                 "metric/test/group_by/foobar/method/linear/step=450",
+                 NULL },
+               26,
+               {
+                       0, 50, 100, 101, 200, 201, 250, 301, 900, 901, 1000, 1001, 1500,
+                       2000, 2001, 2200, 8000, 8001, 8200, 8500, 9000, 9001,
+                       10000, 10001, 10500, 15000,
+               },
+               10,
+               24,
+               { { { INTMAX_MIN, 500 }, 8 },
+                 { { 500, 950 }, 2 },
+                 { { 950, 1400 }, 2 },
+                 { { 1400, 1850 }, 1 },
+                 { { 1850, 2300 }, 3 },
+                 { { 2300, 2750 }, 0 },
+                 { { 2750, 3200 }, 0 },
+                 { { 3200, 3650 }, 0 },
+                 { { 3650, 4100 }, 0 },
+                 { { 4100, 4550 }, 0 },
+                 { { 4550, 5000 }, 0 },
+                 { { 5000, 5450 }, 0 },
+                 { { 5450, 5900 }, 0 },
+                 { { 5900, 6350 }, 0 },
+                 { { 6350, 6800 }, 0 },
+                 { { 6800, 7250 }, 0 },
+                 { { 7250, 7700 }, 0 },
+                 { { 7700, 8150 }, 2 },
+                 { { 8150, 8600 }, 2 },
+                 { { 8600, 9050 }, 2 },
+                 { { 9050, 9500 }, 0 },
+                 { { 9500, 9950 }, 0 },
+                 { { 9950, 10000 }, 1 },
+                 { { 10000, INTMAX_MAX }, 3 },
+               }
+       },
        {
                /* start at 0 */
                (const char *const []){