random: add helpers for random numbers with given floor or range
authorJason A. Donenfeld <Jason@zx2c4.com>
Thu, 20 Oct 2022 05:19:35 +0000 (23:19 -0600)
committerJason A. Donenfeld <Jason@zx2c4.com>
Fri, 18 Nov 2022 01:15:12 +0000 (02:15 +0100)
Now that we have get_random_u32_below(), it's nearly trivial to make
inline helpers to compute get_random_u32_above() and
get_random_u32_inclusive(), which will help clean up open coded loops
and manual computations throughout the tree.

One snag is that in order to make get_random_u32_inclusive() operate on
closed intervals, we have to do some (unlikely) special case handling if
get_random_u32_inclusive(0, U32_MAX) is called. The least expensive way
of doing this is actually to adjust the slowpath of
get_random_u32_below() to have its undefined 0 result just return the
output of get_random_u32(). We can make this basically free by calling
get_random_u32() before the branch, so that the branch latency gets
interleaved.

Cc: stable@vger.kernel.org # to ease future backports that use this api
Reviewed-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
drivers/char/random.c
include/linux/random.h

index 6f323344d0b9d64764f70105ec8881daea3c3c7f..f5868dddbb618cbb891f475207055a67144413d9 100644 (file)
@@ -161,6 +161,8 @@ EXPORT_SYMBOL(wait_for_random_bytes);
  *     u16 get_random_u16()
  *     u32 get_random_u32()
  *     u32 get_random_u32_below(u32 ceil)
+ *     u32 get_random_u32_above(u32 floor)
+ *     u32 get_random_u32_inclusive(u32 floor, u32 ceil)
  *     u64 get_random_u64()
  *     unsigned long get_random_long()
  *
@@ -522,7 +524,21 @@ u32 __get_random_u32_below(u32 ceil)
         * of `-ceil % ceil` is analogous to `2^32 % ceil`, but is computable
         * in 32-bits.
         */
-       u64 mult = (u64)ceil * get_random_u32();
+       u32 rand = get_random_u32();
+       u64 mult;
+
+       /*
+        * This function is technically undefined for ceil == 0, and in fact
+        * for the non-underscored constant version in the header, we build bug
+        * on that. But for the non-constant case, it's convenient to have that
+        * evaluate to being a straight call to get_random_u32(), so that
+        * get_random_u32_inclusive() can work over its whole range without
+        * undefined behavior.
+        */
+       if (unlikely(!ceil))
+               return rand;
+
+       mult = (u64)ceil * rand;
        if (unlikely((u32)mult < ceil)) {
                u32 bound = -ceil % ceil;
                while (unlikely((u32)mult < bound))
index 3a82c0a8bc465f7a5a0f32e14a98d9b51ee8bd32..bd954ecbef901756a021d5fdc1dc6a6a0c41544f 100644 (file)
@@ -91,6 +91,31 @@ static inline u32 get_random_u32_below(u32 ceil)
        }
 }
 
+/*
+ * Returns a random integer in the interval (floor, U32_MAX], with uniform
+ * distribution, suitable for all uses. Fastest when floor is a constant, but
+ * still fast for variable floor as well.
+ */
+static inline u32 get_random_u32_above(u32 floor)
+{
+       BUILD_BUG_ON_MSG(__builtin_constant_p(floor) && floor == U32_MAX,
+                        "get_random_u32_above() must take floor < U32_MAX");
+       return floor + 1 + get_random_u32_below(U32_MAX - floor);
+}
+
+/*
+ * Returns a random integer in the interval [floor, ceil], with uniform
+ * distribution, suitable for all uses. Fastest when floor and ceil are
+ * constant, but still fast for variable floor and ceil as well.
+ */
+static inline u32 get_random_u32_inclusive(u32 floor, u32 ceil)
+{
+       BUILD_BUG_ON_MSG(__builtin_constant_p(floor) && __builtin_constant_p(ceil) &&
+                        (floor > ceil || ceil - floor == U32_MAX),
+                        "get_random_u32_inclusive() must take floor <= ceil");
+       return floor + get_random_u32_below(ceil - floor + 1);
+}
+
 /*
  * On 64-bit architectures, protect against non-terminated C string overflows
  * by zeroing out the first byte of the canary; this leaves 56 bits of entropy.