Commit 6f98a4bf authored by Jason A. Donenfeld's avatar Jason A. Donenfeld

random: block in /dev/urandom

This topic has come up countless times, and usually doesn't go anywhere.
This time I thought I'd bring it up with a slightly narrower focus,
updated for some developments over the last three years: we finally can
make /dev/urandom always secure, in light of the fact that our RNG is
now always seeded.

Ever since Linus' 50ee7529 ("random: try to actively add entropy
rather than passively wait for it"), the RNG does a haveged-style jitter
dance around the scheduler, in order to produce entropy (and credit it)
for the case when we're stuck in wait_for_random_bytes(). How ever you
feel about the Linus Jitter Dance is beside the point: it's been there
for three years and usually gets the RNG initialized in a second or so.

As a matter of fact, this is what happens currently when people use
getrandom(). It's already there and working, and most people have been
using it for years without realizing.

So, given that the kernel has grown this mechanism for seeding itself
from nothing, and that this procedure happens pretty fast, maybe there's
no point any longer in having /dev/urandom give insecure bytes. In the
past we didn't want the boot process to deadlock, which was
understandable. But now, in the worst case, a second goes by, and the
problem is resolved. It seems like maybe we're finally at a point when
we can get rid of the infamous "urandom read hole".

The one slight drawback is that the Linus Jitter Dance relies on random_
get_entropy() being implemented. The first lines of try_to_generate_
entropy() are:

	stack.now = random_get_entropy();
	if (stack.now == random_get_entropy())
		return;

On most platforms, random_get_entropy() is simply aliased to get_cycles().
The number of machines without a cycle counter or some other
implementation of random_get_entropy() in 2022, which can also run a
mainline kernel, and at the same time have a both broken and out of date
userspace that relies on /dev/urandom never blocking at boot is thought
to be exceedingly low. And to be clear: those museum pieces without
cycle counters will continue to run Linux just fine, and even
/dev/urandom will be operable just like before; the RNG just needs to be
seeded first through the usual means, which should already be the case
now.

On systems that really do want unseeded randomness, we already offer
getrandom(GRND_INSECURE), which is in use by, e.g., systemd for seeding
their hash tables at boot. Nothing in this commit would affect
GRND_INSECURE, and it remains the means of getting those types of random
numbers.

This patch goes a long way toward eliminating a long overdue userspace
crypto footgun. After several decades of endless user confusion, we will
finally be able to say, "use any single one of our random interfaces and
you'll be fine. They're all the same. It doesn't matter." And that, I
think, is really something. Finally all of those blog posts and
disagreeing forums and contradictory articles will all become correct
about whatever they happened to recommend, and along with it, a whole
class of vulnerabilities eliminated.

With very minimal downside, we're finally in a position where we can
make this change.

Cc: Dinh Nguyen <dinguyen@kernel.org>
Cc: Nick Hu <nickhu@andestech.com>
Cc: Max Filippov <jcmvbkbc@gmail.com>
Cc: Palmer Dabbelt <palmer@dabbelt.com>
Cc: David S. Miller <davem@davemloft.net>
Cc: Yoshinori Sato <ysato@users.sourceforge.jp>
Cc: Michal Simek <monstr@monstr.eu>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Guo Ren <guoren@kernel.org>
Cc: Geert Uytterhoeven <geert@linux-m68k.org>
Cc: Joshua Kinard <kumba@gentoo.org>
Cc: David Laight <David.Laight@aculab.com>
Cc: Dominik Brodowski <linux@dominikbrodowski.net>
Cc: Eric Biggers <ebiggers@google.com>
Cc: Ard Biesheuvel <ardb@kernel.org>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Kees Cook <keescook@chromium.org>
Cc: Lennart Poettering <mzxreary@0pointer.de>
Cc: Konstantin Ryabitsev <konstantin@linuxfoundation.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Theodore Ts'o <tytso@mit.edu>
Signed-off-by: default avatarJason A. Donenfeld <Jason@zx2c4.com>
parent c2a7de4f
...@@ -707,7 +707,7 @@ static const struct memdev { ...@@ -707,7 +707,7 @@ static const struct memdev {
[5] = { "zero", 0666, &zero_fops, FMODE_NOWAIT }, [5] = { "zero", 0666, &zero_fops, FMODE_NOWAIT },
[7] = { "full", 0666, &full_fops, 0 }, [7] = { "full", 0666, &full_fops, 0 },
[8] = { "random", 0666, &random_fops, 0 }, [8] = { "random", 0666, &random_fops, 0 },
[9] = { "urandom", 0666, &urandom_fops, 0 }, [9] = { "urandom", 0666, &random_fops, 0 },
#ifdef CONFIG_PRINTK #ifdef CONFIG_PRINTK
[11] = { "kmsg", 0644, &kmsg_fops, 0 }, [11] = { "kmsg", 0644, &kmsg_fops, 0 },
#endif #endif
......
...@@ -89,17 +89,14 @@ static LIST_HEAD(random_ready_list); ...@@ -89,17 +89,14 @@ static LIST_HEAD(random_ready_list);
/* Control how we warn userspace. */ /* Control how we warn userspace. */
static struct ratelimit_state unseeded_warning = static struct ratelimit_state unseeded_warning =
RATELIMIT_STATE_INIT("warn_unseeded_randomness", HZ, 3); RATELIMIT_STATE_INIT("warn_unseeded_randomness", HZ, 3);
static struct ratelimit_state urandom_warning =
RATELIMIT_STATE_INIT("warn_urandom_randomness", HZ, 3);
static int ratelimit_disable __read_mostly; static int ratelimit_disable __read_mostly;
module_param_named(ratelimit_disable, ratelimit_disable, int, 0644); module_param_named(ratelimit_disable, ratelimit_disable, int, 0644);
MODULE_PARM_DESC(ratelimit_disable, "Disable random ratelimit suppression"); MODULE_PARM_DESC(ratelimit_disable, "Disable random ratelimit suppression");
/* /*
* Returns whether or not the input pool has been seeded and thus guaranteed * Returns whether or not the input pool has been seeded and thus guaranteed
* to supply cryptographically secure random numbers. This applies to: the * to supply cryptographically secure random numbers. This applies to
* /dev/urandom device, the get_random_bytes function, and the get_random_{u32, * get_random_bytes() and get_random_{u32,u64,int,long}().
* ,u64,int,long} family of functions.
* *
* Returns: true if the input pool has been seeded. * Returns: true if the input pool has been seeded.
* false if the input pool has not been seeded. * false if the input pool has not been seeded.
...@@ -115,10 +112,10 @@ static void try_to_generate_entropy(void); ...@@ -115,10 +112,10 @@ static void try_to_generate_entropy(void);
/* /*
* Wait for the input pool to be seeded and thus guaranteed to supply * Wait for the input pool to be seeded and thus guaranteed to supply
* cryptographically secure random numbers. This applies to: the /dev/urandom * cryptographically secure random numbers. This applies to
* device, the get_random_bytes function, and the get_random_{u32,u64,int,long} * get_random_bytes() and get_random_{u32,u64,int,long}(). Using any
* family of functions. Using any of these functions without first calling * of these functions without first calling this function means that
* this function forfeits the guarantee of security. * the returned numbers might not be cryptographically secure.
* *
* Returns: 0 if the input pool has been seeded. * Returns: 0 if the input pool has been seeded.
* -ERESTARTSYS if the function was interrupted by a signal. * -ERESTARTSYS if the function was interrupted by a signal.
...@@ -256,10 +253,10 @@ static void _warn_unseeded_randomness(const char *func_name, void *caller, void ...@@ -256,10 +253,10 @@ static void _warn_unseeded_randomness(const char *func_name, void *caller, void
* unsigned long get_random_long() * unsigned long get_random_long()
* *
* These interfaces will return the requested number of random bytes * These interfaces will return the requested number of random bytes
* into the given buffer or as a return value. This is equivalent to * into the given buffer or as a return value. The returned numbers are
* a read from /dev/urandom. The integer family of functions may be * the same as those of getrandom(0). The integer family of functions may
* higher performance for one-off random integers, because they do a * be higher performance for one-off random integers, because they do a
* bit of buffering. * bit of buffering and do not invoke reseeding.
* *
*********************************************************************/ *********************************************************************/
...@@ -336,11 +333,6 @@ static void crng_reseed(void) ...@@ -336,11 +333,6 @@ static void crng_reseed(void)
unseeded_warning.missed); unseeded_warning.missed);
unseeded_warning.missed = 0; unseeded_warning.missed = 0;
} }
if (urandom_warning.missed) {
pr_notice("%d urandom warning(s) missed due to ratelimiting\n",
urandom_warning.missed);
urandom_warning.missed = 0;
}
} }
} }
...@@ -993,10 +985,8 @@ int __init rand_initialize(void) ...@@ -993,10 +985,8 @@ int __init rand_initialize(void)
pr_notice("crng init done (trusting CPU's manufacturer)\n"); pr_notice("crng init done (trusting CPU's manufacturer)\n");
} }
if (ratelimit_disable) { if (ratelimit_disable)
urandom_warning.interval = 0;
unseeded_warning.interval = 0; unseeded_warning.interval = 0;
}
return 0; return 0;
} }
...@@ -1386,20 +1376,16 @@ static void try_to_generate_entropy(void) ...@@ -1386,20 +1376,16 @@ static void try_to_generate_entropy(void)
* getrandom(2) is the primary modern interface into the RNG and should * getrandom(2) is the primary modern interface into the RNG and should
* be used in preference to anything else. * be used in preference to anything else.
* *
* Reading from /dev/random has the same functionality as calling * Reading from /dev/random and /dev/urandom both have the same effect
* getrandom(2) with flags=0. In earlier versions, however, it had * as calling getrandom(2) with flags=0. (In earlier versions, however,
* vastly different semantics and should therefore be avoided, to * they each had different semantics.)
* prevent backwards compatibility issues.
*
* Reading from /dev/urandom has the same functionality as calling
* getrandom(2) with flags=GRND_INSECURE. Because it does not block
* waiting for the RNG to be ready, it should not be used.
* *
* Writing to either /dev/random or /dev/urandom adds entropy to * Writing to either /dev/random or /dev/urandom adds entropy to
* the input pool but does not credit it. * the input pool but does not credit it.
* *
* Polling on /dev/random indicates when the RNG is initialized, on * Polling on /dev/random or /dev/urandom indicates when the RNG
* the read side, and when it wants new entropy, on the write side. * is initialized, on the read side, and when it wants new entropy,
* on the write side.
* *
* Both /dev/random and /dev/urandom have the same set of ioctls for * Both /dev/random and /dev/urandom have the same set of ioctls for
* adding entropy, getting the entropy count, zeroing the count, and * adding entropy, getting the entropy count, zeroing the count, and
...@@ -1484,21 +1470,6 @@ static ssize_t random_write(struct file *file, const char __user *buffer, ...@@ -1484,21 +1470,6 @@ static ssize_t random_write(struct file *file, const char __user *buffer,
return (ssize_t)count; return (ssize_t)count;
} }
static ssize_t urandom_read(struct file *file, char __user *buf, size_t nbytes,
loff_t *ppos)
{
static int maxwarn = 10;
if (!crng_ready() && maxwarn > 0) {
maxwarn--;
if (__ratelimit(&urandom_warning))
pr_notice("%s: uninitialized urandom read (%zd bytes read)\n",
current->comm, nbytes);
}
return get_random_bytes_user(buf, nbytes);
}
static ssize_t random_read(struct file *file, char __user *buf, size_t nbytes, static ssize_t random_read(struct file *file, char __user *buf, size_t nbytes,
loff_t *ppos) loff_t *ppos)
{ {
...@@ -1585,15 +1556,6 @@ const struct file_operations random_fops = { ...@@ -1585,15 +1556,6 @@ const struct file_operations random_fops = {
.llseek = noop_llseek, .llseek = noop_llseek,
}; };
const struct file_operations urandom_fops = {
.read = urandom_read,
.write = random_write,
.unlocked_ioctl = random_ioctl,
.compat_ioctl = compat_ptr_ioctl,
.fasync = random_fasync,
.llseek = noop_llseek,
};
/******************************************************************** /********************************************************************
* *
......
...@@ -44,7 +44,7 @@ extern void del_random_ready_callback(struct random_ready_callback *rdy); ...@@ -44,7 +44,7 @@ extern void del_random_ready_callback(struct random_ready_callback *rdy);
extern size_t __must_check get_random_bytes_arch(void *buf, size_t nbytes); extern size_t __must_check get_random_bytes_arch(void *buf, size_t nbytes);
#ifndef MODULE #ifndef MODULE
extern const struct file_operations random_fops, urandom_fops; extern const struct file_operations random_fops;
#endif #endif
u32 get_random_u32(void); u32 get_random_u32(void);
......
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