]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
[Fix] upstream: bail out of get_random when only candidate is excluded
authorVsevolod Stakhov <vsevolod@rspamd.com>
Fri, 1 May 2026 08:03:55 +0000 (09:03 +0100)
committerVsevolod Stakhov <vsevolod@rspamd.com>
Fri, 1 May 2026 08:03:55 +0000 (09:03 +0100)
rspamd_upstream_get_random looped forever when alive->len == 1 and the
single survivor matched the 'except' argument. Front-gate the empty and
single-survivor cases explicitly; the unbounded loop only runs for
n >= 2 where it is guaranteed to terminate.

src/libutil/upstream.c

index a341407c29140af13daf328f7d9dd81a4aa0be1d..9aa0dd7f6a78c5e8089593ca6740b44af192b3bb 100644 (file)
@@ -2008,13 +2008,23 @@ static struct upstream *
 rspamd_upstream_get_random(struct upstream_list *ups,
                                                   struct upstream *except)
 {
-       for (;;) {
-               unsigned int idx = ottery_rand_range(ups->alive->len - 1);
-               struct upstream *up;
+       unsigned int n = ups->alive->len;
+       struct upstream *up;
+
+       if (n == 0) {
+               return NULL;
+       }
+       if (n == 1) {
+               up = g_ptr_array_index(ups->alive, 0);
+               return (except != NULL && up == except) ? NULL : up;
+       }
 
+       /* n >= 2: at most one excluded, retry-on-collision is bounded */
+       for (;;) {
+               unsigned int idx = ottery_rand_range(n - 1);
                up = g_ptr_array_index(ups->alive, idx);
 
-               if (except && up == except) {
+               if (except != NULL && up == except) {
                        continue;
                }