/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 |