linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/2] random: fix some data races
@ 2021-12-20 22:41 Eric Biggers
  2021-12-20 22:41 ` [PATCH v2 1/2] random: fix data race on crng_node_pool Eric Biggers
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Eric Biggers @ 2021-12-20 22:41 UTC (permalink / raw)
  To: Theodore Ts'o, Jason A . Donenfeld 
  Cc: linux-kernel, linux-crypto, Paul E . McKenney

This series fixes some data races in random.c.

Changed v1 => v2:
   - Remove unneeded 'inline' keywords
   - Use READ_ONCE() instead of smp_load_acquire()
   - Updated commit message
   - Added patch to fix data race on crng init time

Eric Biggers (2):
  random: fix data race on crng_node_pool
  random: fix data race on crng init time

 drivers/char/random.c | 61 +++++++++++++++++++++++--------------------
 1 file changed, 33 insertions(+), 28 deletions(-)

-- 
2.34.1


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

* [PATCH v2 1/2] random: fix data race on crng_node_pool
  2021-12-20 22:41 [PATCH v2 0/2] random: fix some data races Eric Biggers
@ 2021-12-20 22:41 ` Eric Biggers
  2021-12-20 22:41 ` [PATCH v2 2/2] random: fix data race on crng init time Eric Biggers
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Eric Biggers @ 2021-12-20 22:41 UTC (permalink / raw)
  To: Theodore Ts'o, Jason A . Donenfeld 
  Cc: linux-kernel, linux-crypto, Paul E . McKenney, stable

From: Eric Biggers <ebiggers@google.com>

extract_crng() and crng_backtrack_protect() load crng_node_pool with a
plain load, which causes undefined behavior if do_numa_crng_init()
modifies it concurrently.

Fix this by using READ_ONCE().  Note: as per the previous discussion
https://lore.kernel.org/lkml/20211219025139.31085-1-ebiggers@kernel.org/T/#u,
READ_ONCE() is believed to be sufficient here, and it was requested that
it be used here instead of smp_load_acquire().

Also change do_numa_crng_init() to set crng_node_pool using
cmpxchg_release() instead of mb() + cmpxchg(), as the former is
sufficient here but is more lightweight.

Fixes: 1e7f583af67b ("random: make /dev/urandom scalable for silly userspace programs")
Cc: stable@vger.kernel.org
Signed-off-by: Eric Biggers <ebiggers@google.com>
---
 drivers/char/random.c | 42 ++++++++++++++++++++++--------------------
 1 file changed, 22 insertions(+), 20 deletions(-)

diff --git a/drivers/char/random.c b/drivers/char/random.c
index 605969ed0f96..1294b4527cdd 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -843,8 +843,8 @@ static void do_numa_crng_init(struct work_struct *work)
 		crng_initialize_secondary(crng);
 		pool[i] = crng;
 	}
-	mb();
-	if (cmpxchg(&crng_node_pool, NULL, pool)) {
+	/* pairs with READ_ONCE() in select_crng() */
+	if (cmpxchg_release(&crng_node_pool, NULL, pool) != NULL) {
 		for_each_node(i)
 			kfree(pool[i]);
 		kfree(pool);
@@ -857,8 +857,26 @@ static void numa_crng_init(void)
 {
 	schedule_work(&numa_crng_init_work);
 }
+
+static struct crng_state *select_crng(void)
+{
+	struct crng_state **pool;
+	int nid = numa_node_id();
+
+	/* pairs with cmpxchg_release() in do_numa_crng_init() */
+	pool = READ_ONCE(crng_node_pool);
+	if (pool && pool[nid])
+		return pool[nid];
+
+	return &primary_crng;
+}
 #else
 static void numa_crng_init(void) {}
+
+static struct crng_state *select_crng(void)
+{
+	return &primary_crng;
+}
 #endif
 
 /*
@@ -1005,15 +1023,7 @@ static void _extract_crng(struct crng_state *crng,
 
 static void extract_crng(__u8 out[CHACHA_BLOCK_SIZE])
 {
-	struct crng_state *crng = NULL;
-
-#ifdef CONFIG_NUMA
-	if (crng_node_pool)
-		crng = crng_node_pool[numa_node_id()];
-	if (crng == NULL)
-#endif
-		crng = &primary_crng;
-	_extract_crng(crng, out);
+	_extract_crng(select_crng(), out);
 }
 
 /*
@@ -1042,15 +1052,7 @@ static void _crng_backtrack_protect(struct crng_state *crng,
 
 static void crng_backtrack_protect(__u8 tmp[CHACHA_BLOCK_SIZE], int used)
 {
-	struct crng_state *crng = NULL;
-
-#ifdef CONFIG_NUMA
-	if (crng_node_pool)
-		crng = crng_node_pool[numa_node_id()];
-	if (crng == NULL)
-#endif
-		crng = &primary_crng;
-	_crng_backtrack_protect(crng, tmp, used);
+	_crng_backtrack_protect(select_crng(), tmp, used);
 }
 
 static ssize_t extract_crng_user(void __user *buf, size_t nbytes)
-- 
2.34.1


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

* [PATCH v2 2/2] random: fix data race on crng init time
  2021-12-20 22:41 [PATCH v2 0/2] random: fix some data races Eric Biggers
  2021-12-20 22:41 ` [PATCH v2 1/2] random: fix data race on crng_node_pool Eric Biggers
@ 2021-12-20 22:41 ` Eric Biggers
  2021-12-20 23:40 ` [PATCH v2 0/2] random: fix some data races Paul E. McKenney
  2021-12-21 12:15 ` Jason A. Donenfeld
  3 siblings, 0 replies; 5+ messages in thread
From: Eric Biggers @ 2021-12-20 22:41 UTC (permalink / raw)
  To: Theodore Ts'o, Jason A . Donenfeld 
  Cc: linux-kernel, linux-crypto, Paul E . McKenney, stable

From: Eric Biggers <ebiggers@google.com>

_extract_crng() does plain loads of crng->init_time and
crng_global_init_time, which causes undefined behavior if
crng_reseed() and RNDRESEEDCRNG modify these corrently.

Use READ_ONCE() and WRITE_ONCE() to make the behavior defined.

Don't fix the race on crng->init_time by protecting it with crng->lock,
since it's not a problem for duplicate reseedings to occur.  I.e., the
lockless access with READ_ONCE() is fine.

Fixes: d848e5f8e1eb ("random: add new ioctl RNDRESEEDCRNG")
Fixes: e192be9d9a30 ("random: replace non-blocking pool with a Chacha20-based CRNG")
Cc: stable@vger.kernel.org
Signed-off-by: Eric Biggers <ebiggers@google.com>
---
 drivers/char/random.c | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/drivers/char/random.c b/drivers/char/random.c
index 1294b4527cdd..1a7bb66d6a58 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -980,7 +980,7 @@ static void crng_reseed(struct crng_state *crng, struct entropy_store *r)
 		crng->state[i+4] ^= buf.key[i] ^ rv;
 	}
 	memzero_explicit(&buf, sizeof(buf));
-	crng->init_time = jiffies;
+	WRITE_ONCE(crng->init_time, jiffies);
 	spin_unlock_irqrestore(&crng->lock, flags);
 	if (crng == &primary_crng && crng_init < 2) {
 		invalidate_batched_entropy();
@@ -1006,12 +1006,15 @@ static void crng_reseed(struct crng_state *crng, struct entropy_store *r)
 static void _extract_crng(struct crng_state *crng,
 			  __u8 out[CHACHA_BLOCK_SIZE])
 {
-	unsigned long v, flags;
+	unsigned long v, flags, init_time;
 
-	if (crng_ready() &&
-	    (time_after(crng_global_init_time, crng->init_time) ||
-	     time_after(jiffies, crng->init_time + CRNG_RESEED_INTERVAL)))
-		crng_reseed(crng, crng == &primary_crng ? &input_pool : NULL);
+	if (crng_ready()) {
+		init_time = READ_ONCE(crng->init_time);
+		if (time_after(READ_ONCE(crng_global_init_time), init_time) ||
+		    time_after(jiffies, init_time + CRNG_RESEED_INTERVAL))
+			crng_reseed(crng, crng == &primary_crng ?
+				    &input_pool : NULL);
+	}
 	spin_lock_irqsave(&crng->lock, flags);
 	if (arch_get_random_long(&v))
 		crng->state[14] ^= v;
@@ -1951,7 +1954,7 @@ static long random_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
 		if (crng_init < 2)
 			return -ENODATA;
 		crng_reseed(&primary_crng, &input_pool);
-		crng_global_init_time = jiffies - 1;
+		WRITE_ONCE(crng_global_init_time, jiffies - 1);
 		return 0;
 	default:
 		return -EINVAL;
-- 
2.34.1


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

* Re: [PATCH v2 0/2] random: fix some data races
  2021-12-20 22:41 [PATCH v2 0/2] random: fix some data races Eric Biggers
  2021-12-20 22:41 ` [PATCH v2 1/2] random: fix data race on crng_node_pool Eric Biggers
  2021-12-20 22:41 ` [PATCH v2 2/2] random: fix data race on crng init time Eric Biggers
@ 2021-12-20 23:40 ` Paul E. McKenney
  2021-12-21 12:15 ` Jason A. Donenfeld
  3 siblings, 0 replies; 5+ messages in thread
From: Paul E. McKenney @ 2021-12-20 23:40 UTC (permalink / raw)
  To: Eric Biggers
  Cc: Theodore Ts'o, Jason A . Donenfeld , linux-kernel, linux-crypto

On Mon, Dec 20, 2021 at 04:41:55PM -0600, Eric Biggers wrote:
> This series fixes some data races in random.c.
> 
> Changed v1 => v2:
>    - Remove unneeded 'inline' keywords
>    - Use READ_ONCE() instead of smp_load_acquire()
>    - Updated commit message
>    - Added patch to fix data race on crng init time
> 
> Eric Biggers (2):
>   random: fix data race on crng_node_pool
>   random: fix data race on crng init time

From a memory-ordering viewpoint:

Acked-by: Paul E. McKenney <paulmck@kernel.org>

>  drivers/char/random.c | 61 +++++++++++++++++++++++--------------------
>  1 file changed, 33 insertions(+), 28 deletions(-)
> 
> -- 
> 2.34.1
> 

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

* Re: [PATCH v2 0/2] random: fix some data races
  2021-12-20 22:41 [PATCH v2 0/2] random: fix some data races Eric Biggers
                   ` (2 preceding siblings ...)
  2021-12-20 23:40 ` [PATCH v2 0/2] random: fix some data races Paul E. McKenney
@ 2021-12-21 12:15 ` Jason A. Donenfeld
  3 siblings, 0 replies; 5+ messages in thread
From: Jason A. Donenfeld @ 2021-12-21 12:15 UTC (permalink / raw)
  To: Eric Biggers
  Cc: Theodore Ts'o, LKML, Linux Crypto Mailing List, Paul E . McKenney

All applied to crng/random.git. Thanks for the series.

Jason

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

end of thread, other threads:[~2021-12-21 12:16 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-12-20 22:41 [PATCH v2 0/2] random: fix some data races Eric Biggers
2021-12-20 22:41 ` [PATCH v2 1/2] random: fix data race on crng_node_pool Eric Biggers
2021-12-20 22:41 ` [PATCH v2 2/2] random: fix data race on crng init time Eric Biggers
2021-12-20 23:40 ` [PATCH v2 0/2] random: fix some data races Paul E. McKenney
2021-12-21 12:15 ` Jason A. Donenfeld

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).