| From 009ba8568be497c640cab7571f7bfd18345d7b24 Mon Sep 17 00:00:00 2001 |
| From: Eric Biggers <ebiggers@google.com> |
| Date: Mon, 20 Dec 2021 16:41:57 -0600 |
| Subject: random: fix data race on crng init time |
| |
| From: Eric Biggers <ebiggers@google.com> |
| |
| commit 009ba8568be497c640cab7571f7bfd18345d7b24 upstream. |
| |
| _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> |
| Acked-by: Paul E. McKenney <paulmck@kernel.org> |
| Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/char/random.c | 17 ++++++++++------- |
| 1 file changed, 10 insertions(+), 7 deletions(-) |
| |
| --- a/drivers/char/random.c |
| +++ b/drivers/char/random.c |
| @@ -951,7 +951,7 @@ static void crng_reseed(struct crng_stat |
| 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(); |
| @@ -978,12 +978,15 @@ static void crng_reseed(struct crng_stat |
| static void _extract_crng(struct crng_state *crng, |
| __u8 out[CHACHA20_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; |
| @@ -1987,7 +1990,7 @@ static long random_ioctl(struct file *f, |
| 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; |