diff mbox series

[RFC,v0] random: block in /dev/urandom

Message ID 20220211210757.612595-1-Jason@zx2c4.com (mailing list archive)
State New, archived
Headers show
Series [RFC,v0] random: block in /dev/urandom | expand

Commit Message

Jason A. Donenfeld Feb. 11, 2022, 9:07 p.m. UTC
This is very much an RFC patch, or maybe even an RFG -- request for
grumbles. This topic has come up a million times, and usually doesn't go
anywhere. This time I thought I'd bring it up with a slightly narrower
focus. Before you read further, realize that I do not intend to merge
this without there being an appropriate amount of consensus for it and
discussion about it.

Ever since Linus' 50ee7529ec45 ("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(2).

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".

Maybe. And this is why this is a request for grumbles patch: the Linus
Jitter Dance relies on random_get_entropy() returning a cycle counter
value. The first lines of try_to_generate_entropy() are:

	stack.now = random_get_entropy();
	/* Slow counter - or none. Don't even bother */
	if (stack.now == random_get_entropy())
		return;

So it would appear that what seemed initially like a panacea does not in
fact work everywhere. Where doesn't it work?

On every platform, random_get_entropy() is connected to get_cycles(),
except for three: m68k, MIPS, and RISC-V.

On m68k, it looks like this:

        if (mach_random_get_entropy)
                return mach_random_get_entropy();
        return 0;

And mach_random_get_entropy seems to be set in amiga/config.c only.

On MIPS, it looks like this:

        if (can_use_mips_counter(prid))
                return read_c0_count();
        else if (likely(imp != PRID_IMP_R6000 && imp != PRID_IMP_R6000A))
                return read_c0_random();
        else
                return 0;

So it seems like we're okay except for R6000 and R6000A.

Finally on RISC-V, it looks like this:

        if (unlikely(clint_time_val == NULL))
                return 0;
        return get_cycles();

Where clint_time_val is eventually filled in later in boot with
clint_timer_init_dt(). So I assume that's a case where it _eventually_
works, which is probably good enough for our purposes.

I think what this adds up to is that this change would positively affect
everybody, except for _possibly_ negatively affecting poorly configured
non-Amiga m68k systems and the MIPS R6000 and R6000A. Does that analysis
seem correct to folks reading, or did I miss something?

Are there other cases where the cycle counter does exist but is simply
too slow? Perhaps some computer historians can chime in here.

If my general analysis is correct, are these ancient platforms really
worth holding this back? I halfway expect to receive a few thrown
tomatoes, an angry fist, and a "get off my lawn!", and if that's _all_ I
hear, I'll take a hint and we can forget I ever proposed this. As
mentioned, I do not intend to merge this unless there's broad consensus
about it. But on the off chance that people feel differently, perhaps
the Linus Jitter Dance is finally the solution to years of /dev/urandom
kvetching.

Cc: Paul Walmsley <paul.walmsley@sifive.com>
Cc: Palmer Dabbelt <palmer@dabbelt.com>
Cc: Albert Ou <aou@eecs.berkeley.edu>
Cc: linux-riscv@lists.infradead.org
Cc: Geert Uytterhoeven <geert@linux-m68k.org>
Cc: linux-m68k@lists.linux-m68k.org
Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
Cc: linux-mips@vger.kernel.org
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: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Theodore Ts'o <tytso@mit.edu>
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
---
 drivers/char/mem.c          |  2 +-
 drivers/char/random.c       | 68 +++++++++----------------------------
 include/uapi/linux/random.h |  2 +-
 3 files changed, 18 insertions(+), 54 deletions(-)

Comments

Linus Torvalds Feb. 11, 2022, 9:29 p.m. UTC | #1
On Fri, Feb 11, 2022 at 1:08 PM Jason A. Donenfeld <Jason@zx2c4.com> wrote:
>
> Maybe. And this is why this is a request for grumbles patch: the Linus
> Jitter Dance relies on random_get_entropy() returning a cycle counter
> value.

Yeah.

I think this patch is fine for architectures that do have that cycle
counter value.

Considering that the jitter thing has been there for 2.5 years by now,
and nobody has really complained about it (*), I think we can call
that thing a success. And on those architectures where
try_to_generate_entropy() works, removing the code that then does that
GRND_INSECURE makes sense. We just don't have any such case any more.

BUT.

When try_to_generate_entropy() doesn't work, I think you now removed
the possible fallback for user space to say "yeah, just give me best
effort". And you might re-introduce a deadlock as a result.

Those systems are arguably broken from a randomness standpoint - what
the h*ll are you supposed to do if there's nothing generating entropy
- but broken or not, I suspect they still exists. Those horrendous
MIPS things were quite common in embedded networking (routers, access
points - places that *should* care)

Do I have a constructive suggestion for those broken platforms? No, I
don't. That arguably is the reason for GRND_INSECURE existing, and the
reason to keep it around.

Long story short: I like your patch, but I worry that it would cause
problems on broken platforms.

And almost nobody tests those broken platforms: even people who build
new kernels for those embedded networking things probably end up using
said kernels with an existing user space setup - where people have
some existing saved source of pseudo-entropy. So they might not ever
even trigger the "first boot problem" that tends to be the worst case.

I'd be willing to apply such a thing anyway - at some point "worry
about broken platforms" ends up being too weak an excuse not to just
apply it - but I'd like to hear more of a reason for this
simplification. If it's just "slight cleanup", maybe we should just
keep the stupid stuff around as a "doesn't hurt good platforms, might
help broken ones".

               Linus

(*) Honestly, I think all the complaints would have been from the
theoretical posers that don't have any practical suggestions anyway
Jason A. Donenfeld Feb. 11, 2022, 9:56 p.m. UTC | #2
Hey Linus,

One thing I should clarify is that the GRND_INSECURE stuff for
getrandom(2) is a bit of a red herring with regards to the primary
intent of this patch, which is making /dev/urandom, the old char
device, block waiting for entropy.

Right now, we have:

/dev/random = getrandom(0)
/dev/urandom = getrandom(GRND_INSECURE)

This proposal is to make that:

/dev/random = getrandom(0)
/dev/urandom = getrandom(0)

Along the way I also got rid of GRND_INSECURE, but arguably that
should be a separate patch. This here is mostly about making
/dev/urandom block.


On 2/11/22, Linus Torvalds <torvalds@linux-foundation.org> wrote:
> When try_to_generate_entropy() doesn't work, I think you now removed
> the possible fallback for user space to say "yeah, just give me best
> effort". And you might re-introduce a deadlock as a result.
>
> Those systems are arguably broken from a randomness standpoint - what
> the h*ll are you supposed to do if there's nothing generating entropy
> - but broken or not, I suspect they still exists. Those horrendous
> MIPS things were quite common in embedded networking (routers, access
> points - places that *should* care)
>
> Do I have a constructive suggestion for those broken platforms? No, I
> don't. That arguably is the reason for GRND_INSECURE existing, and the
> reason to keep it around.

It sounds like you're proposing a middle way here, which would be to
just call try_to_generate_entropy() (the "Linus Jitter Dance" code) if
!crng_ready() in /dev/urandom and getrandom(GRND_INSECURE). That's
actually a pretty good idea. It wouldn't break anything and would only
make the situation better on most systems. I think I'll probably take
your suggestion there, as it seems uncontroversially to be an
improvement.

Revisiting the original proposal though:

> I'd be willing to apply such a thing anyway - at some point "worry
> about broken platforms" ends up being too weak an excuse not to just
> apply it - but I'd like to hear more of a reason for this
simplification.

The justification for always waiting for randomness and never
returning insecure randomness to userspace isn't so much about
simplifying the code -- this patch isn't very large anyway -- but
rather for simplifying userspace crypto footguns. After several
decades of endless user confusion, we'd 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, would really be
something. Finally all of those blog posts and disagreeing forums and
contradictory articles would all become right about whatever they
recommended. So the one side of this is, "misconfigured userspace in a
computer history museum might run into trouble." The other side is,
"everybody gets good random numbers always." Today's thread's new
addition to that old argument is that your jitter dance makes the
downside a heck of a lot less than it used to be.

What do you make of that line of thought?

Jason
Finn Thain Feb. 11, 2022, 10:01 p.m. UTC | #3
On Fri, 11 Feb 2022, Jason A. Donenfeld wrote:

> + * Reading from /dev/random and /dev/urandom both the same effect as
> + * calling getrandom(2) with flags=0. In earlier versions, however,
> + * they each had vastly different semantics and should therefore be
> + * avoided to prevent backwards compatibility issues.

If the end result "should be avoided", then why bother? IOW, how does this 
improve the ABI? I know you said it's a "panacea" but I'm afraid that's 
not clear to me and the patch description doesn't explain it.
Joshua Kinard Feb. 12, 2022, 11:05 p.m. UTC | #4
On 2/11/2022 16:07, Jason A. Donenfeld wrote:
> This is very much an RFC patch, or maybe even an RFG -- request for
> grumbles. This topic has come up a million times, and usually doesn't go
> anywhere. This time I thought I'd bring it up with a slightly narrower
> focus. Before you read further, realize that I do not intend to merge
> this without there being an appropriate amount of consensus for it and
> discussion about it.
> 
> Ever since Linus' 50ee7529ec45 ("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(2).
> 
> 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".
> 
> Maybe. And this is why this is a request for grumbles patch: the Linus
> Jitter Dance relies on random_get_entropy() returning a cycle counter
> value. The first lines of try_to_generate_entropy() are:
> 
> 	stack.now = random_get_entropy();
> 	/* Slow counter - or none. Don't even bother */
> 	if (stack.now == random_get_entropy())
> 		return;
> 
> So it would appear that what seemed initially like a panacea does not in
> fact work everywhere. Where doesn't it work?
> 
> On every platform, random_get_entropy() is connected to get_cycles(),
> except for three: m68k, MIPS, and RISC-V.
> 

[snip]

> On MIPS, it looks like this:
> 
>         if (can_use_mips_counter(prid))
>                 return read_c0_count();
>         else if (likely(imp != PRID_IMP_R6000 && imp != PRID_IMP_R6000A))
>                 return read_c0_random();
>         else
>                 return 0;
> 
> So it seems like we're okay except for R6000 and R6000A.

The R6000/R6000A CPU only ever existed in systems in the late 1980's that
were fairly large, and I don't think there is a complete, working unit out
there that can actually boot up, let alone boot a Linux kernel.  This check
was probably added as a mental exercise following a processor manual or such.

The old linux-mips wiki even says this:
https://www.linux-mips.org/wiki/R6000

"""
The R6000 is an ECL implementation of the MIPS architecture which was
produced by Bipolar Integrated Technology. The R6000 miroprocessor did
introduce the MIPS II instruction set. Its TLB and cache architecture are
different from all other members of the MIPS family. The R6000 did not
deliver the promised performance benefits, and although it saw some use in
Control Data machines, it quickly disappeared from the mainstream market.
"""

A a quick grep of a recent kernel tree shows this one conditional as the
only user, plus the two defines:

# grep -r "PRID_IMP_R6000" *
arch/mips/include/asm/cpu.h:70:#define PRID_IMP_R6000           0x0300
    /* Same as R3000A  */
arch/mips/include/asm/cpu.h:72:#define PRID_IMP_R6000A          0x0600
arch/mips/include/asm/timex.h:94:       else if (likely(imp !=
PRID_IMP_R6000 && imp != PRID_IMP_R6000A))

I'd say it's better to remove the check and simplify the conditional to
eliminate this corner case.  Maybe keep the #defines around for
documentation, but even that may not be necessary for CPUs that likely don't
exist anymore.


> 
> I think what this adds up to is that this change would positively affect
> everybody, except for _possibly_ negatively affecting poorly configured
> non-Amiga m68k systems and the MIPS R6000 and R6000A. Does that analysis
> seem correct to folks reading, or did I miss something?
> 
> Are there other cases where the cycle counter does exist but is simply
> too slow? Perhaps some computer historians can chime in here.
> 
> If my general analysis is correct, are these ancient platforms really
> worth holding this back? I halfway expect to receive a few thrown
> tomatoes, an angry fist, and a "get off my lawn!", and if that's _all_ I
> hear, I'll take a hint and we can forget I ever proposed this. As
> mentioned, I do not intend to merge this unless there's broad consensus
> about it. But on the off chance that people feel differently, perhaps
> the Linus Jitter Dance is finally the solution to years of /dev/urandom
> kvetching.
Maciej W. Rozycki Feb. 12, 2022, 11:13 p.m. UTC | #5
On Sat, 12 Feb 2022, Joshua Kinard wrote:

> # grep -r "PRID_IMP_R6000" *
> arch/mips/include/asm/cpu.h:70:#define PRID_IMP_R6000           0x0300
>     /* Same as R3000A  */
> arch/mips/include/asm/cpu.h:72:#define PRID_IMP_R6000A          0x0600
> arch/mips/include/asm/timex.h:94:       else if (likely(imp !=
> PRID_IMP_R6000 && imp != PRID_IMP_R6000A))
> 
> I'd say it's better to remove the check and simplify the conditional to
> eliminate this corner case.  Maybe keep the #defines around for
> documentation, but even that may not be necessary for CPUs that likely don't
> exist anymore.

 IIRC Ralf used to have a working R6k machine, but I have no idea what has 
happened to it.  No port of Linux has been made for that system though, 
that's for sure.

  Maciej
Andy Lutomirski Feb. 13, 2022, 3:15 a.m. UTC | #6
On Fri, Feb 11, 2022, at 1:07 PM, Jason A. Donenfeld wrote:
> This is very much an RFC patch, or maybe even an RFG -- request for
> grumbles. This topic has come up a million times, and usually doesn't go
> anywhere. This time I thought I'd bring it up with a slightly narrower
> focus. Before you read further, realize that I do not intend to merge
> this without there being an appropriate amount of consensus for it and
> discussion about it.
>
> Ever since Linus' 50ee7529ec45 ("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.

I dislike this patch for a reason that has nothing to do with security. Somewhere there’s a Linux machine that boots straight to Nethack in a glorious 50ms.  If Nethack gets 256 bits of amazing entropy from /dev/urandom, then the machine’s owner has to play for real. If it repeats the same game on occasion, the owner can be disappointed or amused. If it gets a weak seed that can be brute forced, then the owner can have fun brute forcing it.

If, on the other hand, it waits 750ms for enough jitter entropy to be perfect, it’s a complete fail.  No one wants to wait 750ms to play Nethack.

Replace Nethack with something with a backup camera or a lightbulb, both of which have regulations related to startup time, and there may be a real problem. Keep in mind that some language runtimes randomize their hash table seeds at startup, possibly using /dev/urandom. This patch may break actual, correct, working code.
Lennart Poettering Feb. 14, 2022, 8:53 a.m. UTC | #7
On Fr, 11.02.22 22:07, Jason A. Donenfeld (Jason@zx2c4.com) wrote:

> 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".

So, systemd uses (potentially half-initialized) /dev/urandom for
seeding its hash tables. For that its kinda OK if the random values
have low entropy initially, as we'll automatically reseed when too
many hash collisions happen, and then use a newer (and thus hopefully
better) seed, again acquired through /dev/urandom. i.e. if the seeds
are initially not good enough to thwart hash collision attacks, once
the hash table are actually attacked we'll replace the seeds with
someting better. For that all we need is that the random pool
eventually gets better, that's all.

So for that usecase /dev/urandom behaving the way it so far does is
kinda nice. We need lots of hash tables, from earliest initialization
on, hence the ability to get some seed there reasonably fast is really
good, even if its entropy is initially not as high as we'd want. It's
a good middle ground for us to be able to boot up quickly and not
having to block until the entropy pool is fully initialized, but still
thwart hash table collision attacks.

If you make /dev/urandom block for initialization then this would mean
systemd and its components would start waiting for initialization
(simply because we need hash tables all over the place), i.e you'd
effectively add a second to the boot process of each affected system.

What about AT_RANDOM and /proc/sys/kernel/random/uuid btw, do you
intend to block for that too? If you block for the former it doesn't
really matter what systemd does I guess, given that you already have
to delay invoking PID 1 until you get a good AT_RANDOM.

Lennart

--
Lennart Poettering, Berlin
Jason A. Donenfeld Feb. 14, 2022, 2:05 p.m. UTC | #8
Hi Joshua,

Thanks a lot for the historical background.

On Sun, Feb 13, 2022 at 12:06 AM Joshua Kinard <kumba@gentoo.org> wrote:
> The R6000/R6000A CPU only ever existed in systems in the late 1980's that
> were fairly large, and I don't think there is a complete, working unit out
> there that can actually boot up, let alone boot a Linux kernel.

So from what you've written, it sounds like MIPS is actually not a problem here.

So the only systems we're actually talking about without a good cycle
counter are non-Amiga m68k? If so, that'd be a pretty terrific
finding. It'd mean that this idea can move forward, and we only need
to worry about some m68k museum pieces with misconfigured
userspaces...

Jason
Jason A. Donenfeld Feb. 14, 2022, 2:13 p.m. UTC | #9
Hi Lennart,

On Mon, Feb 14, 2022 at 9:53 AM Lennart Poettering <mzxreary@0pointer.de> wrote:
> So, systemd uses (potentially half-initialized) /dev/urandom for
> seeding its hash tables. For that its kinda OK if the random values
> have low entropy initially, as we'll automatically reseed when too
> many hash collisions happen, and then use a newer (and thus hopefully
> better) seed, again acquired through /dev/urandom. i.e. if the seeds
> are initially not good enough to thwart hash collision attacks, once
> the hash table are actually attacked we'll replace the seeds with
> someting better. For that all we need is that the random pool
> eventually gets better, that's all.
>
> So for that usecase /dev/urandom behaving the way it so far does is
> kinda nice.

Oh that's an interesting point. But that sounds to me like the problem
with this patch is not that it makes /dev/urandom block (its primary
purpose) but that it also removes GRND_INSECURE (a distraction). So
perhaps an improved patch would be something like the below, which
changes /dev/urandom for new kernels but doesn't remove GRND_INSECURE.
Then your hash tables could continue to use GRND_INSECURE and all would
be well.  (And for kernels without getrandom(), they'd just fall back to
/dev/urandom like normal which would have old semantics, so works.)

Jason



---------8<-----------------8<-------------------------------

diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index cc296f0823bd..9f586025dbe6 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -707,7 +707,7 @@ static const struct memdev {
 	 [5] = { "zero", 0666, &zero_fops, FMODE_NOWAIT },
 	 [7] = { "full", 0666, &full_fops, 0 },
 	 [8] = { "random", 0666, &random_fops, 0 },
-	 [9] = { "urandom", 0666, &urandom_fops, 0 },
+	 [9] = { "urandom", 0666, &random_fops, 0 },
 #ifdef CONFIG_PRINTK
 	[11] = { "kmsg", 0644, &kmsg_fops, 0 },
 #endif
diff --git a/drivers/char/random.c b/drivers/char/random.c
index ce199af9bc56..ae4400c48b2f 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -89,8 +89,6 @@ static LIST_HEAD(random_ready_list);
 /* Control how we warn userspace. */
 static struct ratelimit_state unseeded_warning =
 	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;
 module_param_named(ratelimit_disable, ratelimit_disable, int, 0644);
 MODULE_PARM_DESC(ratelimit_disable, "Disable random ratelimit suppression");
@@ -336,11 +334,6 @@ static void crng_reseed(void)
 				  unseeded_warning.missed);
 			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 +986,8 @@ int __init rand_initialize(void)
 		pr_notice("crng init done (trusting CPU's manufacturer)\n");
 	}

-	if (ratelimit_disable) {
-		urandom_warning.interval = 0;
+	if (ratelimit_disable)
 		unseeded_warning.interval = 0;
-	}
 	return 0;
 }

@@ -1387,20 +1378,17 @@ static void try_to_generate_entropy(void)
  * getrandom(2) is the primary modern interface into the RNG and should
  * be used in preference to anything else.
  *
- * Reading from /dev/random has the same functionality as calling
- * getrandom(2) with flags=0. In earlier versions, however, it had
- * vastly different semantics and should therefore be avoided, to
- * 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.
+ * Reading from /dev/random and /dev/urandom both the same effect as
+ * calling getrandom(2) with flags=0. In earlier versions, however,
+ * they each had vastly different semantics and should therefore be
+ * avoided to prevent backwards compatibility issues.
  *
  * Writing to either /dev/random or /dev/urandom adds entropy to
  * the input pool but does not credit it.
  *
- * Polling on /dev/random indicates when the RNG is initialized, on
- * the read side, and when it wants new entropy, on the write side.
+ * Polling on /dev/random or /dev/urandom indicates when the RNG
+ * 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
  * adding entropy, getting the entropy count, zeroing the count, and
@@ -1485,21 +1473,6 @@ static ssize_t random_write(struct file *file, const char __user *buffer,
 	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,
 			   loff_t *ppos)
 {
@@ -1586,15 +1559,6 @@ const struct file_operations random_fops = {
 	.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,
-};
-

 /********************************************************************
  *
Geert Uytterhoeven Feb. 14, 2022, 2:26 p.m. UTC | #10
Hi Jason,

On Mon, Feb 14, 2022 at 3:05 PM Jason A. Donenfeld <Jason@zx2c4.com> wrote:
> On Sun, Feb 13, 2022 at 12:06 AM Joshua Kinard <kumba@gentoo.org> wrote:
> > The R6000/R6000A CPU only ever existed in systems in the late 1980's that
> > were fairly large, and I don't think there is a complete, working unit out
> > there that can actually boot up, let alone boot a Linux kernel.
>
> So from what you've written, it sounds like MIPS is actually not a problem here.
>
> So the only systems we're actually talking about without a good cycle
> counter are non-Amiga m68k? If so, that'd be a pretty terrific
> finding. It'd mean that this idea can move forward, and we only need
> to worry about some m68k museum pieces with misconfigured
> userspaces...

I'm afraid you missed one important detail.  You wrote:

> On every platform, random_get_entropy() is connected to get_cycles(),
> except for three: m68k, MIPS, and RISC-V.

The default implementation in include/asm-generic/timex.h is:

    static inline cycles_t get_cycles(void)
    {
            return 0;
    }

Several architectures do not implement get_cycles(), or implement it
with a variant that's very similar or identical to the generic version.

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds
Lennart Poettering Feb. 14, 2022, 2:53 p.m. UTC | #11
On Mo, 14.02.22 15:13, Jason A. Donenfeld (Jason@zx2c4.com) wrote:

> Hi Lennart,
>
> On Mon, Feb 14, 2022 at 9:53 AM Lennart Poettering <mzxreary@0pointer.de> wrote:
> > So, systemd uses (potentially half-initialized) /dev/urandom for
> > seeding its hash tables. For that its kinda OK if the random values
> > have low entropy initially, as we'll automatically reseed when too
> > many hash collisions happen, and then use a newer (and thus hopefully
> > better) seed, again acquired through /dev/urandom. i.e. if the seeds
> > are initially not good enough to thwart hash collision attacks, once
> > the hash table are actually attacked we'll replace the seeds with
> > someting better. For that all we need is that the random pool
> > eventually gets better, that's all.
> >
> > So for that usecase /dev/urandom behaving the way it so far does is
> > kinda nice.
>
> Oh that's an interesting point. But that sounds to me like the problem
> with this patch is not that it makes /dev/urandom block (its primary
> purpose) but that it also removes GRND_INSECURE (a distraction). So
> perhaps an improved patch would be something like the below, which
> changes /dev/urandom for new kernels but doesn't remove GRND_INSECURE.
> Then your hash tables could continue to use GRND_INSECURE and all would
> be well.  (And for kernels without getrandom(), they'd just fall back to
> /dev/urandom like normal which would have old semantics, so works.)

In fact, systemd already uses getrandom(GRND_INSECURE) for this, if it
is supported, and falls back to /dev/urandom only if it is not. So as
long as GRND_INSECURE remains available we are good.

Lennart

--
Lennart Poettering, Berlin
David Laight Feb. 14, 2022, 2:57 p.m. UTC | #12
From: Geert Uytterhoeven
> Sent: 14 February 2022 14:26
...
> I'm afraid you missed one important detail.  You wrote:
> 
> > On every platform, random_get_entropy() is connected to get_cycles(),
> > except for three: m68k, MIPS, and RISC-V.
> 
> The default implementation in include/asm-generic/timex.h is:
> 
>     static inline cycles_t get_cycles(void)
>     {
>             return 0;
>     }
> 
> Several architectures do not implement get_cycles(), or implement it
> with a variant that's very similar or identical to the generic version.

Add to the list nios2 and old x86 (I think rdtsc is a pentium instruction)
I can't see it in my 386 book, and i don't think 486 added it.

I'm not sure if/when sparc added one.
I don't remember it being there in the late 1980s.

nios2 (soft cpu on Altera/Intel fpga) is annoying.
There is a 'read control register' instruction and plenty of space ones.
But you can't define your own and one isn't a clock counter.
You can add one as the result of a custom instruction.
(Even the same custom instruction that does byteswap.)

	David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)
Finn Thain Feb. 14, 2022, 10:53 p.m. UTC | #13
On Mon, 14 Feb 2022, Jason A. Donenfeld wrote:

> 
> So the only systems we're actually talking about without a good cycle 
> counter are non-Amiga m68k? If so, that'd be a pretty terrific finding. 
> It'd mean that this idea can move forward, and we only need to worry 
> about some m68k museum pieces with misconfigured userspaces...
> 

A processor cycle counter is helpful when mounting a timing attack but my 
museum pieces don't suffer from that problem.

Also, they are and always were immune from spectre, meltdown etc.

You misrepresent those secure hardware designs as being problematic, just 
because of some bad advice on some random blogs about RNG API usage.

Do you have a phone that no longer gets updates from its vendor? Have you 
tried patching it?

Your insecure museum pieces are the real problem, not my secure ones.
diff mbox series

Patch

diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index cc296f0823bd..9f586025dbe6 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -707,7 +707,7 @@  static const struct memdev {
 	 [5] = { "zero", 0666, &zero_fops, FMODE_NOWAIT },
 	 [7] = { "full", 0666, &full_fops, 0 },
 	 [8] = { "random", 0666, &random_fops, 0 },
-	 [9] = { "urandom", 0666, &urandom_fops, 0 },
+	 [9] = { "urandom", 0666, &random_fops, 0 },
 #ifdef CONFIG_PRINTK
 	[11] = { "kmsg", 0644, &kmsg_fops, 0 },
 #endif
diff --git a/drivers/char/random.c b/drivers/char/random.c
index c564f795f68c..868334ea0ce3 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -88,8 +88,6 @@  static LIST_HEAD(random_ready_list);
 /* Control how we warn userspace. */
 static struct ratelimit_state unseeded_warning =
 	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;
 module_param_named(ratelimit_disable, ratelimit_disable, int, 0644);
 MODULE_PARM_DESC(ratelimit_disable, "Disable random ratelimit suppression");
@@ -321,11 +319,6 @@  static void crng_reseed(void)
 				  unseeded_warning.missed);
 			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;
-		}
 	}
 }
 
@@ -978,10 +971,8 @@  int __init rand_initialize(void)
 		pr_notice("crng init done (trusting CPU's manufacturer)\n");
 	}
 
-	if (ratelimit_disable) {
-		urandom_warning.interval = 0;
+	if (ratelimit_disable)
 		unseeded_warning.interval = 0;
-	}
 	return 0;
 }
 
@@ -1363,20 +1354,17 @@  static void try_to_generate_entropy(void)
  * getrandom(2) is the primary modern interface into the RNG and should
  * be used in preference to anything else.
  *
- * Reading from /dev/random has the same functionality as calling
- * getrandom(2) with flags=0. In earlier versions, however, it had
- * vastly different semantics and should therefore be avoided, to
- * 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.
+ * Reading from /dev/random and /dev/urandom both the same effect as
+ * calling getrandom(2) with flags=0. In earlier versions, however,
+ * they each had vastly different semantics and should therefore be
+ * avoided to prevent backwards compatibility issues.
  *
  * Writing to either /dev/random or /dev/urandom adds entropy to
  * the input pool but does not credit it.
  *
- * Polling on /dev/random indicates when the RNG is initialized, on
- * the read side, and when it wants new entropy, on the write side.
+ * Polling on /dev/random or /dev/urandom indicates when the RNG
+ * 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
  * adding entropy, getting the entropy count, zeroing the count, and
@@ -1387,6 +1375,8 @@  static void try_to_generate_entropy(void)
 SYSCALL_DEFINE3(getrandom, char __user *, buf, size_t, count, unsigned int,
 		flags)
 {
+	int ret;
+
 	if (flags & ~(GRND_NONBLOCK | GRND_RANDOM | GRND_INSECURE))
 		return -EINVAL;
 
@@ -1400,15 +1390,13 @@  SYSCALL_DEFINE3(getrandom, char __user *, buf, size_t, count, unsigned int,
 	if (count > INT_MAX)
 		count = INT_MAX;
 
-	if (!(flags & GRND_INSECURE) && !crng_ready()) {
-		int ret;
+	if ((flags & GRND_NONBLOCK) && !crng_ready())
+		return -EAGAIN;
+
+	ret = wait_for_random_bytes();
+	if (ret != 0)
+		return ret;
 
-		if (flags & GRND_NONBLOCK)
-			return -EAGAIN;
-		ret = wait_for_random_bytes();
-		if (unlikely(ret))
-			return ret;
-	}
 	return get_random_bytes_user(buf, count);
 }
 
@@ -1461,21 +1449,6 @@  static ssize_t random_write(struct file *file, const char __user *buffer,
 	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,
 			   loff_t *ppos)
 {
@@ -1562,15 +1535,6 @@  const struct file_operations random_fops = {
 	.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,
-};
-
 
 /********************************************************************
  *
diff --git a/include/uapi/linux/random.h b/include/uapi/linux/random.h
index dcc1b3e6106f..9ec1703f45ad 100644
--- a/include/uapi/linux/random.h
+++ b/include/uapi/linux/random.h
@@ -49,7 +49,7 @@  struct rand_pool_info {
  *
  * GRND_NONBLOCK	Don't block and return EAGAIN instead
  * GRND_RANDOM		No effect
- * GRND_INSECURE	Return non-cryptographic random bytes
+ * GRND_INSECURE	No effect
  */
 #define GRND_NONBLOCK	0x0001
 #define GRND_RANDOM	0x0002