lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [day] [month] [year] [list]
Message-Id: <202003281643.02SGhFHS018988@sdf.org>
Date:   Mon, 25 Mar 2019 01:22:12 -0400
From:   George Spelvin <lkml@....org>
To:     linux-kernel@...r.kernel.org, lkml@....org
Cc:     Daniel Borkmann <daniel@...earbox.net>,
        Stephen Hemminger <stephen@...workplumber.org>,
        Hannes Frederic Sowa <hannes@...essinduktion.org>
Subject: [RFC PATCH v1 22/50] lib/random32: Use random_ready_callback for
 seeding.

The whole seeding code was a mess, and triggered boot-time warnings
about crng_init=0.  Clean it up to use the random_ready_callback
exported from drivers/char/random.c.

Particular changes:
* prandom_reseed_late() was dead code
* All the locking in __prandom_reseed is unnecessary and deleted
* Moved __prandom_timer to a more logical place, in addition to
  other changes.  Sorry for the larger diff.
* Use round_jiffies on the reseed timer
* Use mod_timer is simpler to use than add_timer

Signed-off-by: George Spelvin <lkml@....org>
Cc: Daniel Borkmann <daniel@...earbox.net>
Cc: Stephen Hemminger <stephen@...workplumber.org>
Cc: Hannes Frederic Sowa <hannes@...essinduktion.org>
---
 include/linux/random.h |   1 -
 lib/random32.c         | 157 +++++++++++++++++++----------------------
 2 files changed, 71 insertions(+), 87 deletions(-)

diff --git a/include/linux/random.h b/include/linux/random.h
index 9e30f0bb3e50b..4a325f5cf298f 100644
--- a/include/linux/random.h
+++ b/include/linux/random.h
@@ -112,7 +112,6 @@ unsigned long randomize_page(unsigned long start, unsigned long range);
 u32 prandom_u32(void);
 void prandom_bytes(void *buf, size_t nbytes);
 void prandom_seed(u32 seed);
-void prandom_reseed_late(void);
 
 struct rnd_state {
 	__u32 s[4];
diff --git a/lib/random32.c b/lib/random32.c
index 5d8483243c410..5191da383dd18 100644
--- a/lib/random32.c
+++ b/lib/random32.c
@@ -167,12 +167,17 @@ void prandom_bytes(void *buf, size_t bytes)
 }
 EXPORT_SYMBOL(prandom_bytes);
 
+/*
+ * Prefer arch_get_random_int (x86 RDRAND instruction) over
+ * arch_get_random_seed_int (x86 RDSEED) for this application,
+ * as it's good enough, and cheaper.
+ */
 static u32 __extract_hwseed(void)
 {
 	unsigned int val = 0;
 
-	(void)(arch_get_random_seed_int(&val) ||
-	       arch_get_random_int(&val));
+	(void)(arch_get_random_int(&val) ||
+	       arch_get_random_seed_int(&val));
 
 	return val;
 }
@@ -205,9 +210,7 @@ static u32 mix32(u32 x)
  *	@seed: seed value
  *	@mix_with_hwseed: flag enabling use of hardware entropy, if avaiable
  *
- *	This initializes the state without using the /dev/random pool
- *	(which is assumed to not be ready yet).  If @mix_with_hwseed is
- *	%false, the seeding is repeatable.
+ *	This avoids using /dev/urandom, which is not ready yet.
  *
  *	The test for an all-zero state is arguably pointless.  It is
  *	impossible if @mix_with_hwseed is %false, and has probability of
@@ -215,7 +218,7 @@ static u32 mix32(u32 x)
  */
 void prandom_seed_early(struct rnd_state *state, u32 seed, bool mix_with_hwseed)
 {
-	uint32_t bits;
+	u32 bits;
 
 	do {
 		int i;
@@ -231,6 +234,31 @@ void prandom_seed_early(struct rnd_state *state, u32 seed, bool mix_with_hwseed)
 }
 EXPORT_SYMBOL(prandom_seed_early);
 
+/**
+ *	prandom_init - Initial weak seeding of the prandom_u32() engine
+ */
+static int __init prandom_init(void)
+{
+	int i;
+
+	prandom_state_selftest();
+
+	for_each_possible_cpu(i) {
+		struct rnd_state *state = &per_cpu(net_rand_state, i);
+		u32 weak_seed = mix32(i + jiffies) ^ random_get_entropy();
+
+		prandom_seed_early(state, weak_seed, true);
+	}
+
+	return 0;
+}
+core_initcall(prandom_init);
+
+/*
+ * When /dev/urandom is ready, we reseed the generator completely, then
+ * do periodic minor reseeding.  The following code manages that.
+ */
+
 /**
  *	prandom_seed - add entropy to pseudo random number generator
  *	@entropy: entropy value
@@ -280,52 +308,6 @@ void prandom_seed(u32 entropy)
 }
 EXPORT_SYMBOL(prandom_seed);
 
-/*
- *	Generate some initially weak seeding values to allow
- *	to start the prandom_u32() engine.
- */
-static int __init prandom_init(void)
-{
-	int i;
-
-	prandom_state_selftest();
-
-	for_each_possible_cpu(i) {
-		struct rnd_state *state = &per_cpu(net_rand_state, i);
-		u32 weak_seed = (i + jiffies) ^ random_get_entropy();
-
-		prandom_seed_early(state, weak_seed, true);
-	}
-
-	return 0;
-}
-core_initcall(prandom_init);
-
-static void __prandom_timer(struct timer_list *unused);
-
-static DEFINE_TIMER(seed_timer, __prandom_timer);
-
-static void __prandom_timer(struct timer_list *unused)
-{
-	u32 entropy;
-	unsigned long expires;
-
-	get_random_bytes(&entropy, sizeof(entropy));
-	prandom_seed(entropy);
-
-	/* reseed every ~60 seconds, in [40 .. 80) interval with slack */
-	expires = 40 * HZ + prandom_u32_max(40 * HZ);
-	seed_timer.expires = jiffies + expires;
-
-	add_timer(&seed_timer);
-}
-
-static void __init __prandom_start_seed_timer(void)
-{
-	seed_timer.expires = jiffies + 40 * HZ;
-	add_timer(&seed_timer);
-}
-
 /**
  *	prandom_seed_full_state - Reseed PRNG with full entropy
  *	@pcpu_state: Per-CPU state to update
@@ -351,48 +333,51 @@ void prandom_seed_full_state(struct rnd_state __percpu *pcpu_state)
 }
 EXPORT_SYMBOL(prandom_seed_full_state);
 
+static void __prandom_timer(struct timer_list *unused);
+
+static DEFINE_TIMER(seed_timer, __prandom_timer);
+
+static void __prandom_timer(struct timer_list *unused)
+{
+	unsigned long expires;
+	static bool seeded = false;
+
+	if (seeded) {
+		prandom_seed(get_random_u32());
+	} else if (rng_is_initialized()) {
+		/* First time, do a full reseed */
+		seeded = true;
+		prandom_seed_full_state(&net_rand_state);
+	}
+
+	/* reseed every ~60 seconds, in [40 .. 80) interval with slack */
+	expires = round_jiffies(jiffies + 40 * HZ + prandom_u32_max(40 * HZ));
+	mod_timer(&seed_timer, expires);
+}
+
 /*
- *	Generate better values after random number generator
- *	is fully initialized.
+ * The random ready callback can be called from almost any interrupt.
+ * To avoid worrying about whether it's safe to delay that interrupt
+ * long enough to seed all CPUs, just schedule an immediate timer event.
  */
-static void __prandom_reseed(bool late)
+static void prandom_timer_start(struct random_ready_callback *unused)
 {
-	unsigned long flags;
-	static bool latch = false;
-	static DEFINE_SPINLOCK(lock);
-
-	/* Asking for random bytes might result in bytes getting
-	 * moved into the nonblocking pool and thus marking it
-	 * as initialized. In this case we would double back into
-	 * this function and attempt to do a late reseed.
-	 * Ignore the pointless attempt to reseed again if we're
-	 * already waiting for bytes when the nonblocking pool
-	 * got initialized.
-	 */
-
-	/* only allow initial seeding (late == false) once */
-	if (!spin_trylock_irqsave(&lock, flags))
-		return;
-
-	if (latch && !late)
-		goto out;
-
-	latch = true;
-	prandom_seed_full_state(&net_rand_state);
-out:
-	spin_unlock_irqrestore(&lock, flags);
+	mod_timer(&seed_timer, jiffies);
 }
 
-void prandom_reseed_late(void)
-{
-	__prandom_reseed(true);
-}
+static struct random_ready_callback random_ready = {
+        .func = prandom_timer_start
+};
 
 static int __init prandom_reseed(void)
 {
-	__prandom_reseed(false);
-	__prandom_start_seed_timer();
-	return 0;
+	int ret = add_random_ready_callback(&random_ready);
+
+	if (ret == -EALREADY) {
+		prandom_timer_start(&random_ready);
+		ret = 0;
+	}
+	return ret;
 }
 late_initcall(prandom_reseed);
 
-- 
2.26.0

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ