Coverage Report

Created: 2025-12-14 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dovecot/src/lib/rand.c
Line
Count
Source
1
/* Copyright (c) 2014-2018 Dovecot authors, see the included COPYING file */
2
3
#include "lib.h"
4
#include "randgen.h"
5
6
#if defined(HAVE_ARC4RANDOM) && !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
7
#ifdef HAVE_LIBBSD
8
#include <bsd/stdlib.h>
9
#endif
10
11
uint32_t i_rand(void)
12
{
13
  return arc4random();
14
}
15
16
uint32_t i_rand_limit(uint32_t upper_bound)
17
{
18
  i_assert(upper_bound > 0);
19
20
  return arc4random_uniform(upper_bound);
21
}
22
#else
23
uint32_t i_rand(void)
24
0
{
25
0
  uint32_t value;
26
0
  random_fill(&value, sizeof(value));
27
0
  return value;
28
0
}
29
30
/*
31
 * The following generates a random number in the range [0, upper_bound)
32
 * with each possible value having equal probability of occurring.
33
 *
34
 * This algorithm is not original, but it is dense enough that a detailed
35
 * explanation is in order.
36
 *
37
 * The big problem is that we want a uniformly random values.  If one were
38
 * to do `i_rand() % upper_bound`, the result probability distribution would
39
 * depend on the value of the upper bound.  When the upper bound is a power
40
 * of 2, the distribution is uniform.  If it is not a power of 2, the
41
 * distribution is skewed.
42
 *
43
 * The naive modulo approach breaks down because the division effectively
44
 * splits the whole range of input values into a number of fixed sized
45
 * "buckets", but with non-power-of-2 bound the last bucket is not the full
46
 * size.
47
 *
48
 * To fix this bias, we reduce the input range such that the remaining
49
 * values can be split exactly into equal sized buckets.
50
 *
51
 * For example, let's assume that i_rand() produces a uint8_t to simplify
52
 * the math, and that we want a random number [0, 9] - in other words,
53
 * upper_bound == 10.
54
 *
55
 * `i_rand() % 10` makes buckets 10 numbers wide, but the last bucket is only
56
 * 6 numbers wide (250..255).  Therefore, 0..5 will occur more frequently
57
 * than 6..9.
58
 *
59
 * If we reduce the input range to [0, 250), the result of the mod 10 will
60
 * be uniform.  Interestingly, the same can be accomplished if we reduce the
61
 * input range to [6, 255].
62
 *
63
 * This minimum value can be calculated as: 256 % 10 = 6.
64
 *
65
 * Or more generically: (UINT32_MAX + 1) % upper_bound.
66
 *
67
 * Then, we can pick random numbers until we get one that is >= this
68
 * minimum.  Once we have it, we can simply mod it by the limit to get our
69
 * answer.
70
 *
71
 * For our example of modding by 10, we pick random numbers until they are
72
 * greater than or equal to 6.  Once we have one, we have a value in the
73
 * range [6, 255] which when modded by 10 yields uniformly distributed
74
 * values [0, 9].
75
 *
76
 * There are two things to consider while implementing this algorithm:
77
 *
78
 * 1. Division by 0: Getting called with a 0 upper bound doesn't make sense,
79
 *    therefore we simply assert that the passed in bound is non-zero.
80
 *
81
 * 2. 32-bit performance: The above expression to calculate the minimum
82
 *    value requires 64-bit division.  This generally isn't a problem on
83
 *    64-bit systems, but 32-bit systems often end up calling a software
84
 *    implementation (e.g., `__umoddi3`).  This is undesirable.
85
 *
86
 *    Therefore, we rewrite the expression as:
87
 *
88
 *    ~(upper_bound - 1) % upper_bound
89
 *
90
 *    This is harder to understand, but it is 100% equivalent.
91
 */
92
uint32_t i_rand_limit(uint32_t upper_bound)
93
0
{
94
0
  i_assert(upper_bound > 0);
95
96
0
  uint32_t val;
97
0
  uint32_t min = UNSIGNED_MINUS(upper_bound) % upper_bound;
98
0
  while((val = i_rand()) < min);
99
0
  return val % upper_bound;
100
0
}
101
#endif