All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2] random: add helpers for random numbers with given floor or range
@ 2022-11-17 16:35 Jason A. Donenfeld
  2022-11-17 16:41 ` Randy Dunlap
  0 siblings, 1 reply; 4+ messages in thread
From: Jason A. Donenfeld @ 2022-11-17 16:35 UTC (permalink / raw)
  To: linux-kernel, tytso, kees, linux, ydroneaud, gregkh; +Cc: Jason A. Donenfeld

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_interval(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().

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
---
Changes v1->v2:
- Rename get_random_u32_between() to get_random_u32_inclusive(), and
  make it a closed interval.

 drivers/char/random.c  | 17 ++++++++++++++++-
 include/linux/random.h | 25 +++++++++++++++++++++++++
 2 files changed, 41 insertions(+), 1 deletion(-)

diff --git a/drivers/char/random.c b/drivers/char/random.c
index 6f323344d0b9..140dbd557dbc 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -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 ceil, u32 floor)
  *	u64 get_random_u64()
  *	unsigned long get_random_long()
  *
@@ -522,7 +524,20 @@ 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();
+	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 get_random_u32();
+
+	mult = (u64)ceil * get_random_u32();
 	if (unlikely((u32)mult < ceil)) {
 		u32 bound = -ceil % ceil;
 		while (unlikely((u32)mult < bound))
diff --git a/include/linux/random.h b/include/linux/random.h
index 3a82c0a8bc46..bd954ecbef90 100644
--- a/include/linux/random.h
+++ b/include/linux/random.h
@@ -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.
-- 
2.38.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH v2] random: add helpers for random numbers with given floor or range
  2022-11-17 16:35 [PATCH v2] random: add helpers for random numbers with given floor or range Jason A. Donenfeld
@ 2022-11-17 16:41 ` Randy Dunlap
  2022-11-17 19:26   ` [PATCH v3] " Jason A. Donenfeld
  0 siblings, 1 reply; 4+ messages in thread
From: Randy Dunlap @ 2022-11-17 16:41 UTC (permalink / raw)
  To: Jason A. Donenfeld, linux-kernel, tytso, kees, linux, ydroneaud, gregkh



On 11/17/22 08:35, Jason A. Donenfeld wrote:
> diff --git a/drivers/char/random.c b/drivers/char/random.c
> index 6f323344d0b9..140dbd557dbc 100644
> --- a/drivers/char/random.c
> +++ b/drivers/char/random.c
> @@ -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 ceil, u32 floor)

                                         floor,    ceil)

>   *	u64 get_random_u64()
>   *	unsigned long get_random_long()

-- 
~Randy

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH v3] random: add helpers for random numbers with given floor or range
  2022-11-17 16:41 ` Randy Dunlap
@ 2022-11-17 19:26   ` Jason A. Donenfeld
  2022-11-17 21:48     ` Kees Cook
  0 siblings, 1 reply; 4+ messages in thread
From: Jason A. Donenfeld @ 2022-11-17 19:26 UTC (permalink / raw)
  To: linux-kernel, tytso, kees, linux, ydroneaud, gregkh, rdunlap
  Cc: Jason A. Donenfeld

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_interval(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.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
---
v2->v3:
- Fix doc comment argument order.
- Interleave !ceil check with get_random_u32() call to make branch ~free.

 drivers/char/random.c  | 18 +++++++++++++++++-
 include/linux/random.h | 25 +++++++++++++++++++++++++
 2 files changed, 42 insertions(+), 1 deletion(-)

diff --git a/drivers/char/random.c b/drivers/char/random.c
index 6f323344d0b9..f5868dddbb61 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -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))
diff --git a/include/linux/random.h b/include/linux/random.h
index 3a82c0a8bc46..bd954ecbef90 100644
--- a/include/linux/random.h
+++ b/include/linux/random.h
@@ -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.
-- 
2.38.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH v3] random: add helpers for random numbers with given floor or range
  2022-11-17 19:26   ` [PATCH v3] " Jason A. Donenfeld
@ 2022-11-17 21:48     ` Kees Cook
  0 siblings, 0 replies; 4+ messages in thread
From: Kees Cook @ 2022-11-17 21:48 UTC (permalink / raw)
  To: Jason A. Donenfeld
  Cc: linux-kernel, tytso, kees, linux, ydroneaud, gregkh, rdunlap

On Thu, Nov 17, 2022 at 08:26:20PM +0100, Jason A. Donenfeld wrote:
> 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_interval(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.
> 
> Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>

I really like these -- unambiguous! :) Thanks for adjusting this API.

Reviewed-by: Kees Cook <keescook@chromium.org>

-Kees

-- 
Kees Cook

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2022-11-17 21:48 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-11-17 16:35 [PATCH v2] random: add helpers for random numbers with given floor or range Jason A. Donenfeld
2022-11-17 16:41 ` Randy Dunlap
2022-11-17 19:26   ` [PATCH v3] " Jason A. Donenfeld
2022-11-17 21:48     ` Kees Cook

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.