Commit 3638bd90 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'random-5.18-rc2-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/crng/random

Pull random number generator fixes from Jason Donenfeld:

 - Another fixup to the fast_init/crng_init split, this time in how much
   entropy is being credited, from Jan Varho.

 - As discussed, we now opportunistically call try_to_generate_entropy()
   in /dev/urandom reads, as a replacement for the reverted commit. I
   opted to not do the more invasive wait_for_random_bytes() change at
   least for now, preferring to do something smaller and more obvious
   for the time being, but maybe that can be revisited as things evolve
   later.

 - Userspace can use FUSE or userfaultfd or simply move a process to
   idle priority in order to make a read from the random device never
   complete, which breaks forward secrecy, fixed by overwriting
   sensitive bytes early on in the function.

 - Jann Horn noticed that /dev/urandom reads were only checking for
   pending signals if need_resched() was true, a bug going back to the
   genesis commit, now fixed by always checking for signal_pending() and
   calling cond_resched(). This explains various noticeable signal
   delivery delays I've seen in programs over the years that do long
   reads from /dev/urandom.

 - In order to be more like other devices (e.g. /dev/zero) and to
   mitigate the impact of fixing the above bug, which has been around
   forever (users have never really needed to check the return value of
   read() for medium-sized reads and so perhaps many didn't), we now
   move signal checking to the bottom part of the loop, and do so every
   PAGE_SIZE-bytes.

* tag 'random-5.18-rc2-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/crng/random:
  random: check for signals every PAGE_SIZE chunk of /dev/[u]random
  random: check for signal_pending() outside of need_resched() check
  random: do not allow user to keep crng key around on stack
  random: opportunistically initialize on /dev/urandom reads
  random: do not split fast init input in add_hwgenerator_randomness()
parents 640b5037 e3c1c4fd
...@@ -437,11 +437,8 @@ static void crng_make_state(u32 chacha_state[CHACHA_STATE_WORDS], ...@@ -437,11 +437,8 @@ static void crng_make_state(u32 chacha_state[CHACHA_STATE_WORDS],
* This shouldn't be set by functions like add_device_randomness(), * This shouldn't be set by functions like add_device_randomness(),
* where we can't trust the buffer passed to it is guaranteed to be * where we can't trust the buffer passed to it is guaranteed to be
* unpredictable (so it might not have any entropy at all). * unpredictable (so it might not have any entropy at all).
*
* Returns the number of bytes processed from input, which is bounded
* by CRNG_INIT_CNT_THRESH if account is true.
*/ */
static size_t crng_pre_init_inject(const void *input, size_t len, bool account) static void crng_pre_init_inject(const void *input, size_t len, bool account)
{ {
static int crng_init_cnt = 0; static int crng_init_cnt = 0;
struct blake2s_state hash; struct blake2s_state hash;
...@@ -452,18 +449,15 @@ static size_t crng_pre_init_inject(const void *input, size_t len, bool account) ...@@ -452,18 +449,15 @@ static size_t crng_pre_init_inject(const void *input, size_t len, bool account)
spin_lock_irqsave(&base_crng.lock, flags); spin_lock_irqsave(&base_crng.lock, flags);
if (crng_init != 0) { if (crng_init != 0) {
spin_unlock_irqrestore(&base_crng.lock, flags); spin_unlock_irqrestore(&base_crng.lock, flags);
return 0; return;
} }
if (account)
len = min_t(size_t, len, CRNG_INIT_CNT_THRESH - crng_init_cnt);
blake2s_update(&hash, base_crng.key, sizeof(base_crng.key)); blake2s_update(&hash, base_crng.key, sizeof(base_crng.key));
blake2s_update(&hash, input, len); blake2s_update(&hash, input, len);
blake2s_final(&hash, base_crng.key); blake2s_final(&hash, base_crng.key);
if (account) { if (account) {
crng_init_cnt += len; crng_init_cnt += min_t(size_t, len, CRNG_INIT_CNT_THRESH - crng_init_cnt);
if (crng_init_cnt >= CRNG_INIT_CNT_THRESH) { if (crng_init_cnt >= CRNG_INIT_CNT_THRESH) {
++base_crng.generation; ++base_crng.generation;
crng_init = 1; crng_init = 1;
...@@ -474,8 +468,6 @@ static size_t crng_pre_init_inject(const void *input, size_t len, bool account) ...@@ -474,8 +468,6 @@ static size_t crng_pre_init_inject(const void *input, size_t len, bool account)
if (crng_init == 1) if (crng_init == 1)
pr_notice("fast init done\n"); pr_notice("fast init done\n");
return len;
} }
static void _get_random_bytes(void *buf, size_t nbytes) static void _get_random_bytes(void *buf, size_t nbytes)
...@@ -531,7 +523,6 @@ EXPORT_SYMBOL(get_random_bytes); ...@@ -531,7 +523,6 @@ EXPORT_SYMBOL(get_random_bytes);
static ssize_t get_random_bytes_user(void __user *buf, size_t nbytes) static ssize_t get_random_bytes_user(void __user *buf, size_t nbytes)
{ {
bool large_request = nbytes > 256;
ssize_t ret = 0; ssize_t ret = 0;
size_t len; size_t len;
u32 chacha_state[CHACHA_STATE_WORDS]; u32 chacha_state[CHACHA_STATE_WORDS];
...@@ -540,22 +531,23 @@ static ssize_t get_random_bytes_user(void __user *buf, size_t nbytes) ...@@ -540,22 +531,23 @@ static ssize_t get_random_bytes_user(void __user *buf, size_t nbytes)
if (!nbytes) if (!nbytes)
return 0; return 0;
len = min_t(size_t, 32, nbytes); /*
crng_make_state(chacha_state, output, len); * Immediately overwrite the ChaCha key at index 4 with random
* bytes, in case userspace causes copy_to_user() below to sleep
if (copy_to_user(buf, output, len)) * forever, so that we still retain forward secrecy in that case.
return -EFAULT; */
nbytes -= len; crng_make_state(chacha_state, (u8 *)&chacha_state[4], CHACHA_KEY_SIZE);
buf += len; /*
ret += len; * However, if we're doing a read of len <= 32, we don't need to
* use chacha_state after, so we can simply return those bytes to
while (nbytes) { * the user directly.
if (large_request && need_resched()) { */
if (signal_pending(current)) if (nbytes <= CHACHA_KEY_SIZE) {
break; ret = copy_to_user(buf, &chacha_state[4], nbytes) ? -EFAULT : nbytes;
schedule(); goto out_zero_chacha;
} }
do {
chacha20_block(chacha_state, output); chacha20_block(chacha_state, output);
if (unlikely(chacha_state[12] == 0)) if (unlikely(chacha_state[12] == 0))
++chacha_state[13]; ++chacha_state[13];
...@@ -569,10 +561,18 @@ static ssize_t get_random_bytes_user(void __user *buf, size_t nbytes) ...@@ -569,10 +561,18 @@ static ssize_t get_random_bytes_user(void __user *buf, size_t nbytes)
nbytes -= len; nbytes -= len;
buf += len; buf += len;
ret += len; ret += len;
BUILD_BUG_ON(PAGE_SIZE % CHACHA_BLOCK_SIZE != 0);
if (!(ret % PAGE_SIZE) && nbytes) {
if (signal_pending(current))
break;
cond_resched();
} }
} while (nbytes);
memzero_explicit(chacha_state, sizeof(chacha_state));
memzero_explicit(output, sizeof(output)); memzero_explicit(output, sizeof(output));
out_zero_chacha:
memzero_explicit(chacha_state, sizeof(chacha_state));
return ret; return ret;
} }
...@@ -1141,11 +1141,8 @@ void add_hwgenerator_randomness(const void *buffer, size_t count, ...@@ -1141,11 +1141,8 @@ void add_hwgenerator_randomness(const void *buffer, size_t count,
size_t entropy) size_t entropy)
{ {
if (unlikely(crng_init == 0 && entropy < POOL_MIN_BITS)) { if (unlikely(crng_init == 0 && entropy < POOL_MIN_BITS)) {
size_t ret = crng_pre_init_inject(buffer, count, true); crng_pre_init_inject(buffer, count, true);
mix_pool_bytes(buffer, ret); mix_pool_bytes(buffer, count);
count -= ret;
buffer += ret;
if (!count || crng_init == 0)
return; return;
} }
...@@ -1545,6 +1542,13 @@ static ssize_t urandom_read(struct file *file, char __user *buf, size_t nbytes, ...@@ -1545,6 +1542,13 @@ static ssize_t urandom_read(struct file *file, char __user *buf, size_t nbytes,
{ {
static int maxwarn = 10; static int maxwarn = 10;
/*
* Opportunistically attempt to initialize the RNG on platforms that
* have fast cycle counters, but don't (for now) require it to succeed.
*/
if (!crng_ready())
try_to_generate_entropy();
if (!crng_ready() && maxwarn > 0) { if (!crng_ready() && maxwarn > 0) {
maxwarn--; maxwarn--;
if (__ratelimit(&urandom_warning)) if (__ratelimit(&urandom_warning))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment