Module Name: src
Committed By: martin
Date: Fri Aug 11 14:35:26 UTC 2023
Modified Files:
src/etc [netbsd-10]: security
src/etc/defaults [netbsd-10]: rc.conf
src/lib/libc/gen [netbsd-10]: getentropy.3
src/lib/libc/sys [netbsd-10]: getrandom.2
src/share/man/man4 [netbsd-10]: rnd.4
src/share/man/man5 [netbsd-10]: rc.conf.5
src/share/man/man7 [netbsd-10]: entropy.7
src/share/man/man9 [netbsd-10]: rnd.9
src/sys/crypto/cprng_fast [netbsd-10]: cprng_fast.c
src/sys/dev/pci [netbsd-10]: hifn7751.c ubsec.c viornd.c
src/sys/kern [netbsd-10]: kern_clock.c kern_entropy.c subr_cprng.c
subr_prf.c
src/sys/sys [netbsd-10]: rndio.h rndsource.h
src/tests/lib/libc/sys [netbsd-10]: t_getrandom.c
Log Message:
Pull up following revision(s) (requested by riastradh in ticket #319):
sys/dev/pci/ubsec.c: revision 1.64
sys/dev/pci/hifn7751.c: revision 1.82
lib/libc/gen/getentropy.3: revision 1.5
lib/libc/gen/getentropy.3: revision 1.6
share/man/man4/rnd.4: revision 1.41
lib/libc/sys/getrandom.2: revision 1.2
lib/libc/sys/getrandom.2: revision 1.3
share/man/man5/rc.conf.5: revision 1.193
share/man/man7/entropy.7: revision 1.5
share/man/man7/entropy.7: revision 1.6
share/man/man7/entropy.7: revision 1.7
share/man/man7/entropy.7: revision 1.8
etc/security: revision 1.130
share/man/man7/entropy.7: revision 1.9
etc/security: revision 1.131
sys/crypto/cprng_fast/cprng_fast.c: revision 1.19
sys/sys/rndio.h: revision 1.3
tests/lib/libc/sys/t_getrandom.c: revision 1.5
etc/defaults/rc.conf: revision 1.164
etc/defaults/rc.conf: revision 1.165
sys/sys/rndsource.h: revision 1.10
sys/kern/kern_entropy.c: revision 1.62
sys/kern/kern_entropy.c: revision 1.63
sys/kern/kern_entropy.c: revision 1.64
sys/kern/subr_cprng.c: revision 1.44
sys/kern/kern_entropy.c: revision 1.65
sys/kern/kern_clock.c: revision 1.149
sys/dev/pci/viornd.c: revision 1.22
share/man/man9/rnd.9: revision 1.32
sys/kern/subr_prf.c: revision 1.202
sys/sys/rndsource.h: revision 1.8
sys/sys/rndsource.h: revision 1.9
share/man/man7/entropy.7: revision 1.10
1. Reinstate netbsd<=9 entropy estimator to unblock /dev/random, in
parallel with assessment of only confident entropy sources (seed,
HWRNG) for security warnings like sshd keys in motd and daily
insecurity report.
2. Make multiuser boot wait for first /dev/random output soon after
loading a seed and configuring rndctl, so that getentropy(3) meets
its contract starting early at boot without introducing blocking
paths that could cause hangs in init(8) or single-user mode.
Operators can choose to disable this wait in rc.conf.
3. Fix some bugs left over from reducing the global entropy lock from
a spin lock at IPL_VM to an adaptive lock at IPL_SOFTSERIAL.
4. Update man pages.
To generate a diff of this commit:
cvs rdiff -u -r1.129 -r1.129.2.1 src/etc/security
cvs rdiff -u -r1.162 -r1.162.2.1 src/etc/defaults/rc.conf
cvs rdiff -u -r1.4 -r1.4.2.1 src/lib/libc/gen/getentropy.3
cvs rdiff -u -r1.1 -r1.1.6.1 src/lib/libc/sys/getrandom.2
cvs rdiff -u -r1.40 -r1.40.2.1 src/share/man/man4/rnd.4
cvs rdiff -u -r1.192 -r1.192.2.1 src/share/man/man5/rc.conf.5
cvs rdiff -u -r1.4 -r1.4.2.1 src/share/man/man7/entropy.7
cvs rdiff -u -r1.31 -r1.31.2.1 src/share/man/man9/rnd.9
cvs rdiff -u -r1.18 -r1.18.4.1 src/sys/crypto/cprng_fast/cprng_fast.c
cvs rdiff -u -r1.80 -r1.80.4.1 src/sys/dev/pci/hifn7751.c
cvs rdiff -u -r1.62 -r1.62.4.1 src/sys/dev/pci/ubsec.c
cvs rdiff -u -r1.18.4.1 -r1.18.4.2 src/sys/dev/pci/viornd.c
cvs rdiff -u -r1.148 -r1.148.4.1 src/sys/kern/kern_clock.c
cvs rdiff -u -r1.57.4.3 -r1.57.4.4 src/sys/kern/kern_entropy.c
cvs rdiff -u -r1.43 -r1.43.4.1 src/sys/kern/subr_cprng.c
cvs rdiff -u -r1.196.2.1 -r1.196.2.2 src/sys/kern/subr_prf.c
cvs rdiff -u -r1.2 -r1.2.50.1 src/sys/sys/rndio.h
cvs rdiff -u -r1.7 -r1.7.20.1 src/sys/sys/rndsource.h
cvs rdiff -u -r1.4 -r1.4.2.1 src/tests/lib/libc/sys/t_getrandom.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/etc/security
diff -u src/etc/security:1.129 src/etc/security:1.129.2.1
--- src/etc/security:1.129 Thu Nov 4 12:40:00 2021
+++ src/etc/security Fri Aug 11 14:35:25 2023
@@ -1,6 +1,6 @@
#!/bin/sh -
#
-# $NetBSD: security,v 1.129 2021/11/04 12:40:00 nia Exp $
+# $NetBSD: security,v 1.129.2.1 2023/08/11 14:35:25 martin Exp $
# from: @(#)security 8.1 (Berkeley) 6/9/93
#
@@ -195,8 +195,7 @@ done | mtree -CM -k all > $SPECIALSPEC |
# Check for enough entropy.
#
if checkyesno check_entropy; then
- if ! dd if=/dev/random iflag=nonblock of=/dev/null bs=1 count=1 \
- msgfmt=quiet 2>/dev/null; then
+ if [ "$(sysctl -n kern.entropy.needed)" != 0 ]; then
printf '\n'
printf 'Entropy:\n'
printf 'System may need more entropy for cryptography.\n'
Index: src/etc/defaults/rc.conf
diff -u src/etc/defaults/rc.conf:1.162 src/etc/defaults/rc.conf:1.162.2.1
--- src/etc/defaults/rc.conf:1.162 Sun Feb 20 14:42:07 2022
+++ src/etc/defaults/rc.conf Fri Aug 11 14:35:25 2023
@@ -1,4 +1,4 @@
-# $NetBSD: rc.conf,v 1.162 2022/02/20 14:42:07 alnsn Exp $
+# $NetBSD: rc.conf,v 1.162.2.1 2023/08/11 14:35:25 martin Exp $
#
# /etc/defaults/rc.conf --
# default configuration of /etc/rc.conf
@@ -386,9 +386,10 @@ veriexec_flags="-k"
random_seed=YES
# Set to `check' to abort multi-user boot if not enough entropy, or
-# `wait' to wait until enough entropy.
+# `wait' to wait until enough entropy, or `' (empty) to boot without
+# waiting or checking.
#
-entropy=""
+entropy="wait"
# Creating / updating of man page index on boot
makemandb=YES
Index: src/lib/libc/gen/getentropy.3
diff -u src/lib/libc/gen/getentropy.3:1.4 src/lib/libc/gen/getentropy.3:1.4.2.1
--- src/lib/libc/gen/getentropy.3:1.4 Tue May 31 13:42:59 2022
+++ src/lib/libc/gen/getentropy.3 Fri Aug 11 14:35:24 2023
@@ -1,4 +1,4 @@
-.\" $NetBSD: getentropy.3,v 1.4 2022/05/31 13:42:59 riastradh Exp $ $
+.\" $NetBSD: getentropy.3,v 1.4.2.1 2023/08/11 14:35:24 martin Exp $ $
.\"
.\" Copyright (c) 2020 The NetBSD Foundation, Inc.
.\" All rights reserved.
@@ -27,12 +27,12 @@
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
-.Dd May 1, 2020
+.Dd February 28, 2023
.Dt GETENTROPY 3
.Os
.Sh NAME
.Nm getentropy
-.Nd fill a buffer with high quality random data
+.Nd generate uniform random seeds from system entropy for cryptography
.Sh LIBRARY
.Lb libc
.Sh SYNOPSIS
@@ -40,29 +40,34 @@
.Ft int
.Fn getentropy "void *buf" "size_t buflen"
.In limits.h
+.Pp
.Li #define GETENTROPY_MAX 256
.Sh DESCRIPTION
.Pp
The
-.Fn getentropy
-function fills a buffer with high quality random data, suitable for seeding
-cryptographically secure psuedorandom number generators.
+.Nm
+function fills
+.Fa buf
+with exactly
+.Fa buflen
+independent uniform random bytes derived from the system's entropy
+pool.
.Pp
-.Fn getentropy
-is only intended for seeding random number generators and is not intended
-for use by regular code which simply needs secure random data.
-For this purpose, please use
+The output of
+.Nm
+is meant to be unpredictable to an adversary and fit for use in
+cryptography.
+See
+.Sx CAVEATS
+below.
+.Pp
+.Nm
+is meant for seeding random number generators, not for direct use by
+applications; most applications should use
.Xr arc4random 3 .
.Pp
-The maximum value for
-.Li buflen
-is 256 bytes.
-.Sh IMPLEMENTATION NOTES
-.Fn getentropy
-reads from the
-.Xr sysctl 7
-variable
-.Li kern.arandom .
+.Fa buflen
+must be at most 256.
.Sh RETURN VALUES
.Rv -std getentropy
.Sh ERRORS
@@ -75,13 +80,33 @@ The
argument points to an invalid memory address.
.It Bq Er EINVAL
More than 256 bytes were requested.
+.El
+.Sh CAVEATS
+Security can only be guaranteed relative to whatever unpredictable
+physical processes or secret seed material are available to the system;
+see
+.Xr entropy 7 .
+.Pp
+On systems which have no hardware random number generator and which
+have not had secret seed material loaded,
+.Nx
+makes a reasonable effort to incorporate samples from various physical
+processes available to it that might be unpredictable from random
+jitter in timing.
+.Pp
+However, the
+.Nm
+interface alone can make no security guarantees without a physical
+system configuration that includes random number generation hardware or
+secret seed material from such hardware on another machine.
.Sh SEE ALSO
.Xr arc4random 3 ,
-.Xr rnd 4
+.Xr rnd 4 ,
+.Xr entropy 7
.Sh STANDARDS
The
.Fn getentropy
-function is non-standard.
+function is nonstandard.
However, it is likely to be included in the next revision of POSIX.
.Sh HISTORY
The
@@ -90,5 +115,5 @@ function first appeared in
.Ox 5.6 ,
then in
.Fx 12.0 ,
-and
-.Nx 10 .
+and in
+.Nx 10.0 .
Index: src/lib/libc/sys/getrandom.2
diff -u src/lib/libc/sys/getrandom.2:1.1 src/lib/libc/sys/getrandom.2:1.1.6.1
--- src/lib/libc/sys/getrandom.2:1.1 Fri Aug 14 00:53:16 2020
+++ src/lib/libc/sys/getrandom.2 Fri Aug 11 14:35:24 2023
@@ -1,4 +1,4 @@
-.\" $NetBSD: getrandom.2,v 1.1 2020/08/14 00:53:16 riastradh Exp $
+.\" $NetBSD: getrandom.2,v 1.1.6.1 2023/08/11 14:35:24 martin Exp $
.\"
.\" Copyright (c) 2020 The NetBSD Foundation, Inc.
.\" All rights reserved.
@@ -27,12 +27,12 @@
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
-.Dd January 13, 2020
+.Dd March 17, 2022
.Dt GETRANDOM 2
.Os
.Sh NAME
.Nm getrandom
-.Nd random number generation from system entropy
+.Nd generate uniform random seeds from system entropy for cryptography
.Sh LIBRARY
.Lb libc
.Sh SYNOPSIS
@@ -49,90 +49,56 @@ with up to
independent uniform random bytes derived from the system's entropy
pool.
.Pp
-The function may block until the system has full entropy, meaning that
-the system has observed enough noise from physical processes that an
-adversary cannot predict what state it is in:
-.Bl -bullet -compact
-.It
-When the system has only partial entropy, the output of
-.Fn getrandom
-may be predictable.
-.It
-When the system has full entropy, the output is fit for use as
-cryptographic key material.
-.El
+The output of
+.Nm
+is meant to be unpredictable to an adversary and fit for use in
+cryptography.
+See CAVEATS below.
+.Pp
+.Nm
+is meant for seeding random number generators, not for direct use by
+applications; most applications should use
+.Xr arc4random 3 .
+.Pp
+.Nm
+is a nonstandard extension that was added before POSIX began to
+converge on
+.Xr getentropy 2 .
+Applications should avoid
+.Nm
+and use
+.Xr getentropy 2
+instead;
+.Nm
+may be removed from a later release.
+.Pp
+.Nm
+may block indefinitely unless the
+.Dv GRND_INSECURE
+or
+.Dv GRND_NONBLOCK
+flags are specified.
.Pp
The
.Fa flags
argument may be:
-.Bl -tag -offset abcd -width GRND_INSECURE
+.Bl -tag -offset indent -width GRND_INSECURE
.It Li 0
-Block until the system entropy pool has full entropy; then generate
-arbitrarily much data.
-.Em Recommended .
-.Pp
-If interrupted by a signal, may fail with
-.Er EINTR
-or return a short read.
-If successful, guaranteed to return at least 256 bytes even if
-interrupted.
+May block.
+On success, guaranteed to generate the smaller of
+.Fa buflen
+or 256 bytes.
.It Dv GRND_INSECURE
-Do not block; instead fill
-.Fa buf
-with output derived from whatever is in the system entropy pool so
-far.
-Equivalent to reading from
-.Pa /dev/urandom ;
-see
-.Xr rnd 4 .
-.Pp
-If interrupted by a signal, may fail with
-.Er EINTR
-or return a short read.
-If successful, guaranteed to return at least 256 bytes even if
-interrupted.
-.Pp
-Despite the name, this is secure as long as you only do it
-.Em after
-at least one successful call without
-.Dv GRND_INSECURE ,
-such as
-.Li "getrandom(..., 0)"
-or
-.Li "getrandom(..., GRND_RANDOM)" ,
-or after reading at least one byte from
-.Pa /dev/random .
-.Pp
-.Sy WARNING :
-If you use
-.Dv GRND_INSECURE
-.Em before
-the system has full entropy. the output may enable an adversary to
-search the possible states of the entropy pool by brute force, and
-thereby reduce its entropy to zero.
-Thus, incautious use of
-.Dv GRND_INSECURE
-can ruin the security of the whole system.
-.Pp
-.Nx
-attempts to defend against this threat model by resetting the system's
-entropy estimate to zero in this event, requiring gathering full
-entropy again before
-.Pa /dev/random
-or
-.Fn getrandom
-without
-.Dv GRND_INSECURE
-will unblock, but other operating systems may not.
+Never blocks.
+On success, guaranteed to generate the smaller of
+.Fa buflen
+or 256 bytes.
.It Dv GRND_RANDOM
-Block until the system entropy pool has full entropy; then generate a
-small amount of data.
-Equivalent to reading from
-.Pa /dev/random ;
-see
-.Xr rnd 4 .
-This is provided mainly for source compatibility with Linux; there is
-essentially no reason to ever use it.
+Will probably block.
+On success, may generate as little as a single byte of data.
+.Pp
+This is provided for source compatibility with Linux; there is no
+reason to ever use it.
.El
.Pp
The flag
@@ -158,7 +124,7 @@ since
never blocks.
The combination
.Dv GRND_INSECURE Ns Li "|" Ns Li GRND_RANDOM
-is nonsensical and fails with
+always fails with
.Er EINVAL .
.Sh RETURN VALUES
If successful,
@@ -174,7 +140,9 @@ Since
.Li "getrandom(..., 0)"
and
.Li "getrandom(..., GRND_INSECURE)"
-are guaranteed to return at least 256 bytes if successful, it
+are guaranteed to generate
+.Fa buflen
+or 256 bytes, whichever is smaller, if successful, it
is sufficient to use, e.g.,
.Bd -literal -compact
getrandom(buf, 32, 0) == -1
@@ -187,9 +155,8 @@ to detect failure.
However, with
.Dv GRND_RANDOM ,
.Fn getrandom
-may return as little as a single byte if successful.
+may generate as little as a single byte if successful.
.Sh EXAMPLES
-.Sy Recommended usage .
Generate a key for cryptography:
.Bd -literal
uint8_t secretkey[32];
@@ -198,53 +165,23 @@ Generate a key for cryptography:
err(EXIT_FAILURE, "getrandom");
crypto_secretbox_xsalsa20poly1305(..., secretkey);
.Ed
-.Pp
-Other idioms for illustration:
-.Bl -bullet
-.It
-Wait for entropy once, and then generate many keys without waiting:
-.Bd -literal
- struct { uint8_t key[32]; } user[100];
-
- if (getrandom(NULL, 0, 0) == -1)
- err(EXIT_FAILURE, "getrandom");
- for (i = 0; i < 100; i++)
- if (getrandom(user[i].key, sizeof user[i].key,
- GRND_INSECURE) == -1)
- err(EXIT_FAILURE, "getrandom");
-.Ed
-.It
-Twiddle thumbs while waiting for entropy:
-.Bd -literal
- uint8_t secretkey[32];
-
- while (getrandom(secretkey, sizeof secretkey, GRND_NONBLOCK)
- == -1) {
- if (errno != EAGAIN)
- err(EXIT_FAILURE, "getrandom");
- twiddle_thumbs();
- }
- crypto_secretbox_xsalsa20poly1305(..., secretkey);
-.Ed
-.El
-.Pp
-(No examples of
-.Dv GRND_RANDOM
-because it is not useful.)
.Sh ERRORS
.Bl -tag -width Er
.It Bq Er EAGAIN
The
.Dv GRND_NONBLOCK
-flag was specified, and the system entropy pool does not have full
-entropy.
+flag was specified, and
+.Nm
+would have blocked waiting for entropy.
.It Bq Er EINTR
The
.Dv GRND_NONBLOCK
flag was
.Em not
-specified, the system entropy pool does not have full entropy, and the
-process was interrupted by a signal while waiting.
+specified,
+.Nm
+blocked waiting for entropy, and the process was interrupted by a
+signal.
.It Bq Er EINVAL
.Fa flags
contains an unrecognized flag or a nonsensical combination of flags.
@@ -252,20 +189,39 @@ contains an unrecognized flag or a nonse
.Fa buf
points outside the allocated address space.
.El
+.Sh CAVEATS
+Security can only be guaranteed relative to whatever unpredictable
+physical processes or secret seed material are available to the system;
+see
+.Xr entropy 7 .
+.Pp
+On systems which have no hardware random number generator and which
+have not had secret seed material loaded,
+.Nx
+makes a reasonable effort to incorporate samples from various physical
+processes available to it that might be unpredictable from random
+jitter in timing.
+.Pp
+However, the
+.Nm
+interface alone can make no security guarantees without a physical
+system configuration that includes random number generation hardware or
+secret seed material from such hardware on another machine.
.Sh SEE ALSO
-.Xr rnd 4
+.Xr arc4random 3 ,
+.Xr getentropy 3 ,
+.Xr rnd 4 ,
+.Xr entropy 7
+.Sh STANDARDS
+The
+.Nm
+function is a nonstandard Linux extension and will probably never be
+standardized.
.Sh HISTORY
The
.Nm
system call first appeared in Linux 3.17, and was added to
.Nx 10.0 .
-.Sh AUTHORS
-The
-.Nx
-implementation of
-.Nm
-and this man page were written by
-.An Taylor R Campbell Aq Mt [email protected] .
.Sh BUGS
There is no way to multiplex waiting for
.Fn getrandom
@@ -273,11 +229,16 @@ with other I/O in
.Xr select 2 ,
.Xr poll 2 ,
or
-.Xr kqueue 2 .
+.Xr kqueue 2 ,
+or to atomically unmask a set of signals while
+.Nm
+blocks.
Instead, you can wait for a read from
.Pa /dev/random ;
see
.Xr rnd 4 .
.Pp
-.Dv GRND_RANDOM
-is a little silly.
+The
+.Nm
+interface has more options than real-world applications need, with
+confusing and unclear semantics inherited from Linux.
Index: src/share/man/man4/rnd.4
diff -u src/share/man/man4/rnd.4:1.40 src/share/man/man4/rnd.4:1.40.2.1
--- src/share/man/man4/rnd.4:1.40 Sun Mar 20 18:19:57 2022
+++ src/share/man/man4/rnd.4 Fri Aug 11 14:35:24 2023
@@ -1,4 +1,4 @@
-.\" $NetBSD: rnd.4,v 1.40 2022/03/20 18:19:57 riastradh Exp $
+.\" $NetBSD: rnd.4,v 1.40.2.1 2023/08/11 14:35:24 martin Exp $
.\"
.\" Copyright (c) 2014-2020 The NetBSD Foundation, Inc.
.\" All rights reserved.
@@ -27,7 +27,7 @@
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
-.Dd May 1, 2020
+.Dd August 7, 2023
.Dt RND 4
.Os
.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
@@ -613,6 +613,12 @@ A buggy bootloader tried to provide an e
the kernel.
Subsequent seeds will be entered into the entropy pool, but they will
be considered to contribute no entropy.
+.It entropy: best effort
+The system has gathered enough samples from interrupt timings and other
+non-confident sources of entropy for the first time to unblock
+.Pa /dev/random ,
+but it may not have full entropy from a seed or hardware random number
+generator.
.It entropy: ready
The system has full entropy for the first time.
.El
Index: src/share/man/man5/rc.conf.5
diff -u src/share/man/man5/rc.conf.5:1.192 src/share/man/man5/rc.conf.5:1.192.2.1
--- src/share/man/man5/rc.conf.5:1.192 Sun Feb 20 14:43:39 2022
+++ src/share/man/man5/rc.conf.5 Fri Aug 11 14:35:25 2023
@@ -1,4 +1,4 @@
-.\" $NetBSD: rc.conf.5,v 1.192 2022/02/20 14:43:39 alnsn Exp $
+.\" $NetBSD: rc.conf.5,v 1.192.2.1 2023/08/11 14:35:25 martin Exp $
.\"
.\" Copyright (c) 1996 Matthew R. Green
.\" All rights reserved.
@@ -454,11 +454,13 @@ from the output of
Passes
.Sy dmesg_flags .
.It Sy entropy
-A string,
-.Sq Li check
+A string, either
+.Sq Li check ,
+.Sq Li wait ,
or
-.Sq Li wait .
-If set, then during boot-up, after
+.Sq Li ""
+(empty).
+If set and nonempty, then during boot-up, after
.Sy random_seed
and
.Sy rndctl ,
Index: src/share/man/man7/entropy.7
diff -u src/share/man/man7/entropy.7:1.4 src/share/man/man7/entropy.7:1.4.2.1
--- src/share/man/man7/entropy.7:1.4 Sun Mar 20 18:19:58 2022
+++ src/share/man/man7/entropy.7 Fri Aug 11 14:35:25 2023
@@ -1,4 +1,4 @@
-.\" $NetBSD: entropy.7,v 1.4 2022/03/20 18:19:58 riastradh Exp $
+.\" $NetBSD: entropy.7,v 1.4.2.1 2023/08/11 14:35:25 martin Exp $
.\"
.\" Copyright (c) 2021 The NetBSD Foundation, Inc.
.\" All rights reserved.
@@ -24,7 +24,7 @@
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
-.Dd January 4, 2021
+.Dd June 30, 2023
.Dt ENTROPY 7
.Os
.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
@@ -62,11 +62,17 @@ then they could impersonate you.
.Nx
relies on unpredictable secrets to make sure that private user data
stored on nonvolatile media when memory is scarce
-.Pq Xr swapctl 8 , using So Li vm.swap_encrypt=1 Sc ; see Xr sysctl 7
+.Po
+.Xr swapctl 8 ,
+using
+.Ql vm.swap_encrypt=1 ;
+see
+.Xr sysctl 7
+.Pc
cannot be recovered by forensic tools after shutdown.
.El
.\""""""""""""""""""""""""""""""""""""""
-.Ss Entropy in Nx
+.Ss Entropy in NetBSD
.Nx
gathers samples from various kinds of entropy sources, including:
.Bl -bullet -compact
@@ -105,13 +111,8 @@ for security, thanks to modern cryptogra
.Pp
To detect potentially insecure systems,
.Nx
-records how many bits it needs to achieve the full 256 bits, exposed
-via the
-.Xr sysctl 7
-variable
-.Li kern.entropy.needed ,
-and takes measures to alert the operator if there isn't definitely
-enough for security:
+takes measures to alert the operator if there isn't definitely enough
+for security:
.Bl -bullet
.It
.Nx
@@ -119,29 +120,41 @@ issues warnings on the console if there'
programs need it; see
.Xr rnd 4 .
.It
-The daily security report includes an alert if there's not enough
+The
+.Xr motd 5
+has a warning if there was not enough entropy when network daemons such as
+.Xr sshd 8
+first generated keys.
+.It
+The daily security report includes an alert if there's still not enough
entropy; see
.Xr security.conf 5 .
-.It
-The operator can set
-.Sq Li entropy=check
-in
-.Xr rc.conf 5
-so that
-.Nx
-will refuse to boot to multiuser unless there is enough entropy, or set
-.Sq Li entropy=wait
-so that
-.Nx
-will wait for entropy before booting to multiuser (with the caveat that
-it may cause boot to hang forever).
.El
.Pp
-Since it is difficult to confidently model the unpredictability of most
-physical systems, only devices specifically designed to be hardware
-random number generators count toward
-.Nx Ns 's
-estimate of the entropy.
+Since it is hard to know how unpredictable most physical systems are,
+only devices specifically designed to be hardware random number
+generators, or a seed file stored on disk, count toward these alerts.
+.Pp
+At boot,
+.Nx
+will wait, when
+.Ql entropy=wait
+is set in
+.Xr rc.conf 5 ,
+or fail to single-user mode, when
+.Ql entropy=check
+is set, if there is not enough entropy from
+.Em any
+sources, including devices not designed to be unpredictable, such as
+the CPU cycle counter sampled by a periodic timer, provided the samples
+pass a simple filter called the
+.Sq entropy estimator ,
+like other operating systems.
+Sources known to be predictable, which could give a false sense of
+security, can be disabled from unblocking boot by setting
+.Li rndctl_flags
+in
+.Xr rc.conf 5 .
.Pp
Many new computers have hardware random number generators, such as
RDRAND/RDSEED in Intel/AMD CPUs, or ARMv8.5-RNDRRS;
@@ -206,7 +219,7 @@ After adding entropy,
that might be predictable because they were previously generated with
too little entropy.
For example, if
-.Sq Li sshd=YES
+.Ql sshd=YES
is enabled in
.Pa /etc/rc.conf ,
then
@@ -219,7 +232,7 @@ create new ones before allowing anyone t
.Sh DIAGNOSTICS
.Nx
may print the following warnings to the console:
-.Bl -diag -offset indent
+.Bl -diag
.It WARNING: system needs entropy for security; see entropy(7)
Some process tried to draw use entropy from
.Nx ,
Index: src/share/man/man9/rnd.9
diff -u src/share/man/man9/rnd.9:1.31 src/share/man/man9/rnd.9:1.31.2.1
--- src/share/man/man9/rnd.9:1.31 Tue May 17 01:39:57 2022
+++ src/share/man/man9/rnd.9 Fri Aug 11 14:35:25 2023
@@ -1,4 +1,4 @@
-.\" $NetBSD: rnd.9,v 1.31 2022/05/17 01:39:57 riastradh Exp $
+.\" $NetBSD: rnd.9,v 1.31.2.1 2023/08/11 14:35:25 martin Exp $
.\"
.\" Copyright (c) 1997 The NetBSD Foundation, Inc.
.\" All rights reserved.
@@ -35,6 +35,7 @@
.Nm rnd_attach_source ,
.Nm rnd_detach_source ,
.Nm rnd_add_data ,
+.Nm rnd_add_data_intr ,
.Nm rnd_add_data_sync ,
.Nm rnd_add_uint32
.Nd functions to make a device available for entropy collection
@@ -50,6 +51,8 @@
.Ft void
.Fn rnd_add_data "krndsource_t *rnd_source" "void *data" "uint32_t len" "uint32_t entropy"
.Ft void
+.Fn rnd_add_data_intr "krndsource_t *rnd_source" "void *data" "uint32_t len" "uint32_t entropy"
+.Ft void
.Fn rnd_add_data_sync "krndsource_t *rnd_source" "void *data" "uint32_t len" "uint32_t entropy"
.Ft void
.Fn rnd_add_uint32 "krndsource_t *rnd_source" "uint32_t datum"
@@ -82,7 +85,8 @@ Attach the random source with
.Fn rnd_attach_source .
.It
Enter data with
-.Fn rnd_add_data
+.Fn rnd_add_data ,
+.Fn rnd_add_data_intr ,
or
.Fn rnd_add_uint32 ,
or, if in the callback,
@@ -147,7 +151,8 @@ The callback normally does one of two th
Sends a request to a hardware device for entropy and returns.
The hardware will later return data asynchronously by an interrupt, and
the callback will use
-.Fn rnd_add_data
+.Fn rnd_add_data ,
+.Fn rnd_add_data_intr ,
or
.Fn rnd_add_uint32
to add the data to the pool.
@@ -161,9 +166,10 @@ returning, the callback
use
.Fn rnd_add_data_sync ,
not
-.Fn rnd_add_data
+.Fn rnd_add_data ,
+.Fn rnd_add_data_intr \" this works for now but no promises
or
-.Fn rnd_add_uint32 .
+.Fn rnd_add_uint32 . \" this also works for now but no promises
.El
.Pp
.Nm
@@ -285,22 +291,65 @@ be used during a callback as set with
use
.Fn rnd_add_data_sync
instead.
+.Pp
+.Fn rnd_add_data
+.Em must not
+be called from thread context with spin locks held.
+.Pp
+For compatibility,
+.Fn rnd_add_data
+currently
+.Em may
+but
+.Em should not
+be called from interrupt context, possibly with spin locks held.
+However, this may be forbidden in the future; use
+.Fn rnd_add_data_intr
+from interrupt context instead, if the work can't be usefully deferred
+to softint or thread.
+.It Fn rnd_add_data_intr "rnd_source" "data" "len" "entropy"
+Tries to enter
+.Fa len
+bytes at
+.Fa data
+into the entropy pool like
+.Fn rnd_add_data ,
+but if this fills or would overflow a sample buffer, schedules a
+softint to process it and discards an unspecified subset of the data
+while counting zero entropy for the sample.
+.Pp
+.Fn rnd_add_data_intr
+may be called from any context, including hard interrupt context,
+including contexts where spin locks are held, except that it
+.Em must not
+be used during a callback as set with
+.Fn rndsource_setcb ;
+use
+.Fn rnd_add_data_sync
+in that context instead.
.It Fn rnd_add_data_sync "rnd_source" "data" "len" "entropy"
Like
.Fn rnd_add_data ,
but may be used in a callback as set with
.Fn rndsource_setcb .
+Must always be called in thread context.
.It Fn rnd_add_uint32 "rnd_source" "datum"
Equivalent to
-.Li rnd_add_data Ns ( Ns Fa rnd_source , Li & Ns Fa datum , Li 4 , 0 ) .
+.Li rnd_add_data_intr Ns ( Ns Fa rnd_source , Li & Ns Fa datum , Li 4 , 0 ) .
.Pp
.Fn rnd_add_uint32
+may be called from any context, including hard interrupt context,
+including contexts where spin locks are held, except that it
.Em must not
be used during a callback as set with
.Fn rndsource_setcb ;
use
.Fn rnd_add_data_sync
-instead.
+in that context instead.
+.Pp
+.Fn rnd_add_uint32
+is meant for cheaply taking samples from devices that aren't designed
+to be hardware random number generators.
.El
.Sh FILES
These functions are declared in src/sys/sys/rndsource.h and defined in
Index: src/sys/crypto/cprng_fast/cprng_fast.c
diff -u src/sys/crypto/cprng_fast/cprng_fast.c:1.18 src/sys/crypto/cprng_fast/cprng_fast.c:1.18.4.1
--- src/sys/crypto/cprng_fast/cprng_fast.c:1.18 Thu Sep 1 18:32:25 2022
+++ src/sys/crypto/cprng_fast/cprng_fast.c Fri Aug 11 14:35:25 2023
@@ -1,4 +1,4 @@
-/* $NetBSD: cprng_fast.c,v 1.18 2022/09/01 18:32:25 riastradh Exp $ */
+/* $NetBSD: cprng_fast.c,v 1.18.4.1 2023/08/11 14:35:25 martin Exp $ */
/*-
* Copyright (c) 2014 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: cprng_fast.c,v 1.18 2022/09/01 18:32:25 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: cprng_fast.c,v 1.18.4.1 2023/08/11 14:35:25 martin Exp $");
#include <sys/types.h>
#include <sys/param.h>
@@ -58,7 +58,7 @@ struct cprng_fast {
};
static void cprng_fast_init_cpu(void *, void *, struct cpu_info *);
-static void cprng_fast_reseed(struct cprng_fast *);
+static void cprng_fast_reseed(struct cprng_fast **, unsigned);
static void cprng_fast_seed(struct cprng_fast *, const void *);
static void cprng_fast_buf(struct cprng_fast *, void *, unsigned);
@@ -93,6 +93,7 @@ static int
cprng_fast_get(struct cprng_fast **cprngp)
{
struct cprng_fast *cprng;
+ unsigned epoch;
int s;
KASSERT(!cpu_intr_p());
@@ -101,9 +102,10 @@ cprng_fast_get(struct cprng_fast **cprng
*cprngp = cprng = percpu_getref(cprng_fast_percpu);
s = splsoftserial();
- if (__predict_false(cprng->epoch != entropy_epoch())) {
+ epoch = entropy_epoch();
+ if (__predict_false(cprng->epoch != epoch)) {
splx(s);
- cprng_fast_reseed(cprng);
+ cprng_fast_reseed(cprngp, epoch);
s = splsoftserial();
}
@@ -121,13 +123,25 @@ cprng_fast_put(struct cprng_fast *cprng,
}
static void
-cprng_fast_reseed(struct cprng_fast *cprng)
+cprng_fast_reseed(struct cprng_fast **cprngp, unsigned epoch)
{
- unsigned epoch = entropy_epoch();
+ struct cprng_fast *cprng;
uint8_t seed[CPRNG_FAST_SEED_BYTES];
int s;
+ /*
+ * Drop the percpu(9) reference to extract a fresh seed from
+ * the entropy pool. cprng_strong may sleep on an adaptive
+ * lock, which invalidates our percpu(9) reference.
+ *
+ * This may race with reseeding in another thread, which is no
+ * big deal -- worst case, we rewind the entropy epoch here and
+ * cause the next caller to reseed again, and in the end we
+ * just reseed a couple more times than necessary.
+ */
+ percpu_putref(cprng_fast_percpu);
cprng_strong(kern_cprng, seed, sizeof(seed), 0);
+ *cprngp = cprng = percpu_getref(cprng_fast_percpu);
s = splsoftserial();
cprng_fast_seed(cprng, seed);
Index: src/sys/dev/pci/hifn7751.c
diff -u src/sys/dev/pci/hifn7751.c:1.80 src/sys/dev/pci/hifn7751.c:1.80.4.1
--- src/sys/dev/pci/hifn7751.c:1.80 Sun May 22 11:39:27 2022
+++ src/sys/dev/pci/hifn7751.c Fri Aug 11 14:35:24 2023
@@ -1,4 +1,4 @@
-/* $NetBSD: hifn7751.c,v 1.80 2022/05/22 11:39:27 riastradh Exp $ */
+/* $NetBSD: hifn7751.c,v 1.80.4.1 2023/08/11 14:35:24 martin Exp $ */
/* $OpenBSD: hifn7751.c,v 1.179 2020/01/11 21:34:03 cheloha Exp $ */
/*
@@ -47,7 +47,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: hifn7751.c,v 1.80 2022/05/22 11:39:27 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: hifn7751.c,v 1.80.4.1 2023/08/11 14:35:24 martin Exp $");
#include <sys/param.h>
#include <sys/cprng.h>
@@ -655,7 +655,7 @@ hifn_rng(struct hifn_softc *sc)
hexdump(printf, "hifn", num, sizeof num);
#endif
entropybits = NBBY*sizeof(num)/HIFN_RNG_BITSPER;
- rnd_add_data(&sc->sc_rnd_source, num, sizeof(num),
+ rnd_add_data_intr(&sc->sc_rnd_source, num, sizeof(num),
entropybits);
entropybits = MAX(entropybits, 1);
entropybits = MIN(entropybits, sc->sc_rng_needbits);
@@ -693,7 +693,7 @@ hifn_rng(struct hifn_softc *sc)
hexdump(printf, "hifn", num, sizeof num);
#endif
entropybits = NBBY*sizeof(num)/HIFN_RNG_BITSPER;
- rnd_add_data(&sc->sc_rnd_source, num, sizeof num,
+ rnd_add_data_intr(&sc->sc_rnd_source, num, sizeof num,
entropybits);
entropybits = MAX(entropybits, 1);
entropybits = MIN(entropybits, sc->sc_rng_needbits);
Index: src/sys/dev/pci/ubsec.c
diff -u src/sys/dev/pci/ubsec.c:1.62 src/sys/dev/pci/ubsec.c:1.62.4.1
--- src/sys/dev/pci/ubsec.c:1.62 Sat Aug 27 05:35:17 2022
+++ src/sys/dev/pci/ubsec.c Fri Aug 11 14:35:24 2023
@@ -1,4 +1,4 @@
-/* $NetBSD: ubsec.c,v 1.62 2022/08/27 05:35:17 skrll Exp $ */
+/* $NetBSD: ubsec.c,v 1.62.4.1 2023/08/11 14:35:24 martin Exp $ */
/* $FreeBSD: src/sys/dev/ubsec/ubsec.c,v 1.6.2.6 2003/01/23 21:06:43 sam Exp $ */
/* $OpenBSD: ubsec.c,v 1.143 2009/03/27 13:31:30 reyk Exp$ */
@@ -35,7 +35,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ubsec.c,v 1.62 2022/08/27 05:35:17 skrll Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ubsec.c,v 1.62.4.1 2023/08/11 14:35:24 martin Exp $");
#undef UBSEC_DEBUG
@@ -1939,7 +1939,7 @@ ubsec_callback2(struct ubsec_softc *sc,
rng->rng_buf.dma_map->dm_mapsize, BUS_DMASYNC_POSTREAD);
p = (u_int32_t *)rng->rng_buf.dma_vaddr;
i = UBSEC_RNG_BUFSIZ * sizeof(u_int32_t);
- rnd_add_data(&sc->sc_rnd_source, (char *)p, i, i * NBBY);
+ rnd_add_data_intr(&sc->sc_rnd_source, (char *)p, i, i * NBBY);
sc->sc_rng_need -= i;
rng->rng_used = 0;
if (sc->sc_rng_need > 0) {
Index: src/sys/dev/pci/viornd.c
diff -u src/sys/dev/pci/viornd.c:1.18.4.1 src/sys/dev/pci/viornd.c:1.18.4.2
--- src/sys/dev/pci/viornd.c:1.18.4.1 Sat May 13 10:56:10 2023
+++ src/sys/dev/pci/viornd.c Fri Aug 11 14:35:24 2023
@@ -1,4 +1,4 @@
-/* $NetBSD: viornd.c,v 1.18.4.1 2023/05/13 10:56:10 martin Exp $ */
+/* $NetBSD: viornd.c,v 1.18.4.2 2023/08/11 14:35:24 martin Exp $ */
/* $OpenBSD: viornd.c,v 1.1 2014/01/21 21:14:58 sf Exp $ */
/*
@@ -245,8 +245,9 @@ viornd_vq_done(struct virtqueue *vq)
#if VIORND_DEBUG
aprint_normal("%s: got %d bytes of entropy\n", __func__, len);
#endif
- rnd_add_data(&sc->sc_rndsource, sc->sc_buf, VIORND_BUFSIZE,
- VIORND_BUFSIZE * NBBY);
+ /* XXX Shouldn't this be len instead of VIORND_BUFSIZE? */
+ rnd_add_data_intr(&sc->sc_rndsource, sc->sc_buf, VIORND_BUFSIZE,
+ VIORND_BUFSIZE * NBBY);
out:
virtio_dequeue_commit(vsc, vq, slot);
mutex_exit(&sc->sc_mutex);
Index: src/sys/kern/kern_clock.c
diff -u src/sys/kern/kern_clock.c:1.148 src/sys/kern/kern_clock.c:1.148.4.1
--- src/sys/kern/kern_clock.c:1.148 Sat Mar 19 14:34:47 2022
+++ src/sys/kern/kern_clock.c Fri Aug 11 14:35:25 2023
@@ -1,4 +1,4 @@
-/* $NetBSD: kern_clock.c,v 1.148 2022/03/19 14:34:47 riastradh Exp $ */
+/* $NetBSD: kern_clock.c,v 1.148.4.1 2023/08/11 14:35:25 martin Exp $ */
/*-
* Copyright (c) 2000, 2004, 2006, 2007, 2008 The NetBSD Foundation, Inc.
@@ -69,7 +69,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: kern_clock.c,v 1.148 2022/03/19 14:34:47 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: kern_clock.c,v 1.148.4.1 2023/08/11 14:35:25 martin Exp $");
#ifdef _KERNEL_OPT
#include "opt_dtrace.h"
@@ -284,12 +284,14 @@ initclocks(void)
rndsource_setcb(&hardclockrnd.source, clockrnd_get, &hardclockrnd);
rnd_attach_source(&hardclockrnd.source, "hardclock", RND_TYPE_SKEW,
- RND_FLAG_COLLECT_TIME|RND_FLAG_HASCB);
+ RND_FLAG_COLLECT_TIME|RND_FLAG_ESTIMATE_TIME|RND_FLAG_HASCB);
if (stathz) {
rndsource_setcb(&statclockrnd.source, clockrnd_get,
&statclockrnd);
rnd_attach_source(&statclockrnd.source, "statclock",
- RND_TYPE_SKEW, RND_FLAG_COLLECT_TIME|RND_FLAG_HASCB);
+ RND_TYPE_SKEW,
+ (RND_FLAG_COLLECT_TIME|RND_FLAG_ESTIMATE_TIME|
+ RND_FLAG_HASCB));
}
}
Index: src/sys/kern/kern_entropy.c
diff -u src/sys/kern/kern_entropy.c:1.57.4.3 src/sys/kern/kern_entropy.c:1.57.4.4
--- src/sys/kern/kern_entropy.c:1.57.4.3 Tue Aug 1 16:10:59 2023
+++ src/sys/kern/kern_entropy.c Fri Aug 11 14:35:25 2023
@@ -1,4 +1,4 @@
-/* $NetBSD: kern_entropy.c,v 1.57.4.3 2023/08/01 16:10:59 martin Exp $ */
+/* $NetBSD: kern_entropy.c,v 1.57.4.4 2023/08/11 14:35:25 martin Exp $ */
/*-
* Copyright (c) 2019 The NetBSD Foundation, Inc.
@@ -62,20 +62,22 @@
* facilitates an operator explicitly causing everything to
* reseed by sysctl -w kern.entropy.consolidate=1.
*
- * * No entropy estimation based on the sample values, which is a
- * contradiction in terms and a potential source of side
- * channels. It is the responsibility of the driver author to
- * study how predictable the physical source of input can ever
- * be, and to furnish a lower bound on the amount of entropy it
- * has.
- *
* * Entropy depletion is available for testing (or if you're into
* that sort of thing), with sysctl -w kern.entropy.depletion=1;
* the logic to support it is small, to minimize chance of bugs.
+ *
+ * * While cold, a single global entropy pool is available for
+ * entering and extracting, serialized through splhigh/splx.
+ * The per-CPU entropy pool data structures are initialized in
+ * entropy_init and entropy_init_late (separated mainly for
+ * hysterical raisins at this point), but are not used until the
+ * system is warm, at which point access to the global entropy
+ * pool is limited to thread and softint context and serialized
+ * by E->lock.
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: kern_entropy.c,v 1.57.4.3 2023/08/01 16:10:59 martin Exp $");
+__KERNEL_RCSID(0, "$NetBSD: kern_entropy.c,v 1.57.4.4 2023/08/11 14:35:25 martin Exp $");
#include <sys/param.h>
#include <sys/types.h>
@@ -122,6 +124,10 @@ __KERNEL_RCSID(0, "$NetBSD: kern_entropy
#include <machine/cpu_counter.h>
#endif
+#define MINENTROPYBYTES ENTROPY_CAPACITY
+#define MINENTROPYBITS (MINENTROPYBYTES*NBBY)
+#define MINSAMPLES (2*MINENTROPYBITS)
+
/*
* struct entropy_cpu
*
@@ -138,7 +144,8 @@ struct entropy_cpu {
struct evcnt intrtrunc;
} *ec_evcnt;
struct entpool *ec_pool;
- unsigned ec_pending;
+ unsigned ec_bitspending;
+ unsigned ec_samplespending;
bool ec_locked;
};
@@ -161,6 +168,7 @@ struct rndsource_cpu {
unsigned rc_entropybits;
unsigned rc_timesamples;
unsigned rc_datasamples;
+ rnd_delta_t rc_timedelta;
};
/*
@@ -173,8 +181,10 @@ struct rndsource_cpu {
struct {
kmutex_t lock; /* covers all global state */
struct entpool pool; /* global pool for extraction */
- unsigned needed; /* (A) needed globally */
- unsigned pending; /* (A) pending in per-CPU pools */
+ unsigned bitsneeded; /* (A) needed globally */
+ unsigned bitspending; /* pending in per-CPU pools */
+ unsigned samplesneeded; /* (A) needed globally */
+ unsigned samplespending; /* pending in per-CPU pools */
unsigned timestamp; /* (A) time of last consolidation */
unsigned epoch; /* (A) changes when needed -> 0 */
kcondvar_t cv; /* notifies state changes */
@@ -182,20 +192,15 @@ struct {
struct lwp *sourcelock; /* lock on list of sources */
kcondvar_t sourcelock_cv; /* notifies sourcelock release */
LIST_HEAD(,krndsource) sources; /* list of entropy sources */
- enum entropy_stage {
- ENTROPY_COLD = 0, /* single-threaded */
- ENTROPY_WARM, /* multi-threaded at boot before CPUs */
- ENTROPY_HOT, /* multi-threaded multi-CPU */
- } stage;
bool consolidate; /* kick thread to consolidate */
bool seed_rndsource; /* true if seed source is attached */
bool seeded; /* true if seed file already loaded */
} entropy_global __cacheline_aligned = {
/* Fields that must be initialized when the kernel is loaded. */
- .needed = ENTROPY_CAPACITY*NBBY,
+ .bitsneeded = MINENTROPYBITS,
+ .samplesneeded = MINSAMPLES,
.epoch = (unsigned)-1, /* -1 means entropy never consolidated */
.sources = LIST_HEAD_INITIALIZER(entropy_global.sources),
- .stage = ENTROPY_COLD,
};
#define E (&entropy_global) /* declutter */
@@ -249,11 +254,11 @@ static struct sysctllog *entropy_sysctl
static void entropy_init_cpu(void *, void *, struct cpu_info *);
static void entropy_fini_cpu(void *, void *, struct cpu_info *);
static void entropy_account_cpu(struct entropy_cpu *);
-static void entropy_enter(const void *, size_t, unsigned);
-static bool entropy_enter_intr(const void *, size_t, unsigned);
+static void entropy_enter(const void *, size_t, unsigned, bool);
+static bool entropy_enter_intr(const void *, size_t, unsigned, bool);
static void entropy_softintr(void *);
static void entropy_thread(void *);
-static uint32_t entropy_pending(void);
+static bool entropy_pending(void);
static void entropy_pending_cpu(void *, void *, struct cpu_info *);
static void entropy_do_consolidate(void);
static void entropy_consolidate_xc(void *, void *);
@@ -263,8 +268,10 @@ static int sysctl_entropy_gather(SYSCTLF
static void filt_entropy_read_detach(struct knote *);
static int filt_entropy_read_event(struct knote *, long);
static int entropy_request(size_t, int);
+static void rnd_add_data_internal(struct krndsource *, const void *,
+ uint32_t, uint32_t, bool);
static void rnd_add_data_1(struct krndsource *, const void *, uint32_t,
- uint32_t, uint32_t);
+ uint32_t, bool, uint32_t, bool);
static unsigned rndsource_entropybits(struct krndsource *);
static void rndsource_entropybits_cpu(void *, void *, struct cpu_info *);
static void rndsource_to_user(struct krndsource *, rndsource_t *);
@@ -306,12 +313,17 @@ static void
attach_seed_rndsource(void)
{
+ KASSERT(!cpu_intr_p());
+ KASSERT(!cpu_softintr_p());
+ KASSERT(cold);
+
/*
* First called no later than entropy_init, while we are still
* single-threaded, so no need for RUN_ONCE.
*/
- if (E->stage >= ENTROPY_WARM || E->seed_rndsource)
+ if (E->seed_rndsource)
return;
+
rnd_attach_source(&seed_rndsource, "seed", RND_TYPE_UNKNOWN,
RND_FLAG_COLLECT_VALUE);
E->seed_rndsource = true;
@@ -322,7 +334,8 @@ attach_seed_rndsource(void)
*
* Initialize the entropy subsystem. Panic on failure.
*
- * Requires percpu(9) and sysctl(9) to be initialized.
+ * Requires percpu(9) and sysctl(9) to be initialized. Must run
+ * while cold.
*/
static void
entropy_init(void)
@@ -331,7 +344,7 @@ entropy_init(void)
struct krndsource *rs;
unsigned i = 0;
- KASSERT(E->stage == ENTROPY_COLD);
+ KASSERT(cold);
/* Grab some cycle counts early at boot. */
extra[i++] = entropy_timer();
@@ -368,12 +381,24 @@ entropy_init(void)
/* XXX These should maybe not be readable at securelevel>0. */
sysctl_createv(&entropy_sysctllog, 0, &entropy_sysctlroot, NULL,
CTLFLAG_PERMANENT|CTLFLAG_READONLY|CTLFLAG_PRIVATE, CTLTYPE_INT,
- "needed", SYSCTL_DESCR("Systemwide entropy deficit"),
- NULL, 0, &E->needed, 0, CTL_CREATE, CTL_EOL);
+ "needed",
+ SYSCTL_DESCR("Systemwide entropy deficit (bits of entropy)"),
+ NULL, 0, &E->bitsneeded, 0, CTL_CREATE, CTL_EOL);
sysctl_createv(&entropy_sysctllog, 0, &entropy_sysctlroot, NULL,
CTLFLAG_PERMANENT|CTLFLAG_READONLY|CTLFLAG_PRIVATE, CTLTYPE_INT,
- "pending", SYSCTL_DESCR("Entropy pending on CPUs"),
- NULL, 0, &E->pending, 0, CTL_CREATE, CTL_EOL);
+ "pending",
+ SYSCTL_DESCR("Number of bits of entropy pending on CPUs"),
+ NULL, 0, &E->bitspending, 0, CTL_CREATE, CTL_EOL);
+ sysctl_createv(&entropy_sysctllog, 0, &entropy_sysctlroot, NULL,
+ CTLFLAG_PERMANENT|CTLFLAG_READONLY|CTLFLAG_PRIVATE, CTLTYPE_INT,
+ "samplesneeded",
+ SYSCTL_DESCR("Systemwide entropy deficit (samples)"),
+ NULL, 0, &E->samplesneeded, 0, CTL_CREATE, CTL_EOL);
+ sysctl_createv(&entropy_sysctllog, 0, &entropy_sysctlroot, NULL,
+ CTLFLAG_PERMANENT|CTLFLAG_READONLY|CTLFLAG_PRIVATE, CTLTYPE_INT,
+ "samplespending",
+ SYSCTL_DESCR("Number of samples pending on CPUs"),
+ NULL, 0, &E->samplespending, 0, CTL_CREATE, CTL_EOL);
sysctl_createv(&entropy_sysctllog, 0, &entropy_sysctlroot, NULL,
CTLFLAG_PERMANENT|CTLFLAG_READONLY|CTLFLAG_PRIVATE, CTLTYPE_INT,
"epoch", SYSCTL_DESCR("Entropy epoch"),
@@ -403,30 +428,8 @@ entropy_init(void)
/* Enter the boot cycle count to get started. */
extra[i++] = entropy_timer();
KASSERT(i == __arraycount(extra));
- entropy_enter(extra, sizeof extra, 0);
+ entropy_enter(extra, sizeof extra, /*nbits*/0, /*count*/false);
explicit_memset(extra, 0, sizeof extra);
-
- /* We are now ready for multi-threaded operation. */
- E->stage = ENTROPY_WARM;
-}
-
-static void
-entropy_init_late_cpu(void *a, void *b)
-{
- int bound;
-
- /*
- * We're not necessarily in a softint lwp here (xc_broadcast
- * triggers softint on other CPUs, but calls directly on this
- * CPU), so explicitly bind to the current CPU to invoke the
- * softintr -- this lets us have a simpler assertion in
- * entropy_account_cpu. Not necessary to avoid migration
- * because xc_broadcast disables kpreemption anyway, but it
- * doesn't hurt.
- */
- bound = curlwp_bind();
- entropy_softintr(NULL);
- curlwp_bindx(bound);
}
/*
@@ -435,22 +438,22 @@ entropy_init_late_cpu(void *a, void *b)
* Late initialization. Panic on failure.
*
* Requires CPUs to have been detected and LWPs to have started.
+ * Must run while cold.
*/
static void
entropy_init_late(void)
{
- void *sih;
int error;
- KASSERT(E->stage == ENTROPY_WARM);
+ KASSERT(cold);
/*
* Establish the softint at the highest softint priority level.
* Must happen after CPU detection.
*/
- sih = softint_establish(SOFTINT_SERIAL|SOFTINT_MPSAFE,
+ entropy_sih = softint_establish(SOFTINT_SERIAL|SOFTINT_MPSAFE,
&entropy_softintr, NULL);
- if (sih == NULL)
+ if (entropy_sih == NULL)
panic("unable to establish entropy softint");
/*
@@ -462,25 +465,6 @@ entropy_init_late(void)
if (error)
panic("unable to create entropy housekeeping thread: %d",
error);
-
- /*
- * Wait until the per-CPU initialization has hit all CPUs
- * before proceeding to mark the entropy system hot and
- * enabling use of the softint.
- */
- xc_barrier(XC_HIGHPRI);
- E->stage = ENTROPY_HOT;
- atomic_store_relaxed(&entropy_sih, sih);
-
- /*
- * At this point, entering new samples from interrupt handlers
- * will trigger the softint to process them. But there may be
- * some samples that were entered from interrupt handlers
- * before the softint was available. Make sure we process
- * those samples on all CPUs by running the softint logic on
- * all CPUs.
- */
- xc_wait(xc_broadcast(XC_HIGHPRI, entropy_init_late_cpu, NULL, NULL));
}
/*
@@ -496,7 +480,8 @@ entropy_init_cpu(void *ptr, void *cookie
ec->ec_evcnt = kmem_alloc(sizeof(*ec->ec_evcnt), KM_SLEEP);
ec->ec_pool = kmem_zalloc(sizeof(*ec->ec_pool), KM_SLEEP);
- ec->ec_pending = 0;
+ ec->ec_bitspending = 0;
+ ec->ec_samplespending = 0;
ec->ec_locked = false;
/* XXX ci_cpuname may not be initialized early enough. */
@@ -594,6 +579,10 @@ entropy_seed(rndsave_t *seed)
uint8_t digest[SHA1_DIGEST_LENGTH];
bool seeded;
+ KASSERT(!cpu_intr_p());
+ KASSERT(!cpu_softintr_p());
+ KASSERT(cold);
+
/*
* Verify the checksum. If the checksum fails, take the data
* but ignore the entropy estimate -- the file may have been
@@ -627,12 +616,8 @@ entropy_seed(rndsave_t *seed)
attach_seed_rndsource();
/* Test and set E->seeded. */
- if (E->stage >= ENTROPY_WARM)
- mutex_enter(&E->lock);
seeded = E->seeded;
E->seeded = (seed->entropy > 0);
- if (E->stage >= ENTROPY_WARM)
- mutex_exit(&E->lock);
/*
* If we've been seeded, may be re-entering the same seed
@@ -657,23 +642,23 @@ entropy_seed(rndsave_t *seed)
* entropy_bootrequest()
*
* Request entropy from all sources at boot, once config is
- * complete and interrupts are running.
+ * complete and interrupts are running but we are still cold.
*/
void
entropy_bootrequest(void)
{
int error;
- KASSERT(E->stage >= ENTROPY_WARM);
+ KASSERT(!cpu_intr_p());
+ KASSERT(!cpu_softintr_p());
+ KASSERT(cold);
/*
* Request enough to satisfy the maximum entropy shortage.
* This is harmless overkill if the bootloader provided a seed.
*/
- mutex_enter(&E->lock);
- error = entropy_request(ENTROPY_CAPACITY, ENTROPY_WAIT);
- KASSERT(error == 0);
- mutex_exit(&E->lock);
+ error = entropy_request(MINENTROPYBYTES, ENTROPY_WAIT);
+ KASSERTMSG(error == 0, "error=%d", error);
}
/*
@@ -721,7 +706,7 @@ bool
entropy_ready(void)
{
- return atomic_load_relaxed(&E->needed) == 0;
+ return atomic_load_relaxed(&E->bitsneeded) == 0;
}
/*
@@ -746,16 +731,17 @@ entropy_account_cpu(struct entropy_cpu *
{
struct entropy_cpu_lock lock;
struct entropy_cpu *ec0;
- unsigned diff;
+ unsigned bitsdiff, samplesdiff;
- KASSERT(E->stage >= ENTROPY_WARM);
+ KASSERT(!cpu_intr_p());
+ KASSERT(!cold);
KASSERT(curlwp->l_pflag & LP_BOUND);
/*
* If there's no entropy needed, and entropy has been
* consolidated in the last minute, do nothing.
*/
- if (__predict_true(atomic_load_relaxed(&E->needed) == 0) &&
+ if (__predict_true(atomic_load_relaxed(&E->bitsneeded) == 0) &&
__predict_true(!atomic_load_relaxed(&entropy_depletion)) &&
__predict_true((time_uptime - E->timestamp) <= 60))
return;
@@ -767,9 +753,10 @@ entropy_account_cpu(struct entropy_cpu *
mutex_enter(&E->lock);
ec0 = entropy_cpu_get(&lock);
KASSERT(ec0 == ec);
- if (ec->ec_pending == 0) {
+
+ if (ec->ec_bitspending == 0 && ec->ec_samplespending == 0) {
/* Raced with consolidation xcall. Nothing to do. */
- } else if (E->needed != 0 && E->needed <= ec->ec_pending) {
+ } else if (E->bitsneeded != 0 && E->bitsneeded <= ec->ec_bitspending) {
/*
* If we have not yet attained full entropy but we can
* now, do so. This way we disseminate entropy
@@ -783,38 +770,64 @@ entropy_account_cpu(struct entropy_cpu *
/* Transfer from the local pool to the global pool. */
entpool_extract(ec->ec_pool, buf, sizeof buf);
entpool_enter(&E->pool, buf, sizeof buf);
- atomic_store_relaxed(&ec->ec_pending, 0);
- atomic_store_relaxed(&E->needed, 0);
+ atomic_store_relaxed(&ec->ec_bitspending, 0);
+ atomic_store_relaxed(&ec->ec_samplespending, 0);
+ atomic_store_relaxed(&E->bitsneeded, 0);
+ atomic_store_relaxed(&E->samplesneeded, 0);
/* Notify waiters that we now have full entropy. */
entropy_notify();
entropy_immediate_evcnt.ev_count++;
} else {
/* Determine how much we can add to the global pool. */
- KASSERTMSG(E->pending <= ENTROPY_CAPACITY*NBBY,
- "E->pending=%u", E->pending);
- diff = MIN(ec->ec_pending, ENTROPY_CAPACITY*NBBY - E->pending);
+ KASSERTMSG(E->bitspending <= MINENTROPYBITS,
+ "E->bitspending=%u", E->bitspending);
+ bitsdiff = MIN(ec->ec_bitspending,
+ MINENTROPYBITS - E->bitspending);
+ KASSERTMSG(E->samplespending <= MINSAMPLES,
+ "E->samplespending=%u", E->samplespending);
+ samplesdiff = MIN(ec->ec_samplespending,
+ MINSAMPLES - E->samplespending);
/*
* This should make a difference unless we are already
* saturated.
*/
- KASSERTMSG(diff || E->pending == ENTROPY_CAPACITY*NBBY,
- "diff=%u E->pending=%u ec->ec_pending=%u cap=%u",
- diff, E->pending, ec->ec_pending,
- (unsigned)ENTROPY_CAPACITY*NBBY);
+ KASSERTMSG((bitsdiff || samplesdiff ||
+ E->bitspending == MINENTROPYBITS ||
+ E->samplespending == MINSAMPLES),
+ "bitsdiff=%u E->bitspending=%u ec->ec_bitspending=%u"
+ "samplesdiff=%u E->samplespending=%u"
+ " ec->ec_samplespending=%u"
+ " minentropybits=%u minsamples=%u",
+ bitsdiff, E->bitspending, ec->ec_bitspending,
+ samplesdiff, E->samplespending, ec->ec_samplespending,
+ (unsigned)MINENTROPYBITS, (unsigned)MINSAMPLES);
/* Add to the global, subtract from the local. */
- E->pending += diff;
- KASSERT(E->pending);
- KASSERTMSG(E->pending <= ENTROPY_CAPACITY*NBBY,
- "E->pending=%u", E->pending);
- atomic_store_relaxed(&ec->ec_pending, ec->ec_pending - diff);
+ E->bitspending += bitsdiff;
+ KASSERTMSG(E->bitspending <= MINENTROPYBITS,
+ "E->bitspending=%u", E->bitspending);
+ atomic_store_relaxed(&ec->ec_bitspending,
+ ec->ec_bitspending - bitsdiff);
+
+ E->samplespending += samplesdiff;
+ KASSERTMSG(E->samplespending <= MINSAMPLES,
+ "E->samplespending=%u", E->samplespending);
+ atomic_store_relaxed(&ec->ec_samplespending,
+ ec->ec_samplespending - samplesdiff);
+
+ /* One or the other must have gone up from zero. */
+ KASSERT(E->bitspending || E->samplespending);
- if (E->needed <= E->pending) {
+ if (E->bitsneeded <= E->bitspending ||
+ E->samplesneeded <= E->samplespending) {
/*
- * Enough entropy between all the per-CPU
- * pools. Wake up the housekeeping thread.
+ * Enough bits or at least samples between all
+ * the per-CPU pools. Leave a note for the
+ * housekeeping thread to consolidate entropy
+ * next time it wakes up -- and wake it up if
+ * this is the first time, to speed things up.
*
* If we don't need any entropy, this doesn't
* mean much, but it is the only time we ever
@@ -824,14 +837,16 @@ entropy_account_cpu(struct entropy_cpu *
* negligible performance cost.
*/
E->consolidate = true;
- cv_broadcast(&E->cv);
- if (E->needed == 0)
+ if (E->epoch == (unsigned)-1)
+ cv_broadcast(&E->cv);
+ if (E->bitsneeded == 0)
entropy_discretionary_evcnt.ev_count++;
} else {
/* Can't get full entropy. Keep gathering. */
entropy_partial_evcnt.ev_count++;
}
}
+
entropy_cpu_put(&lock, ec);
mutex_exit(&E->lock);
}
@@ -848,8 +863,19 @@ static void
entropy_enter_early(const void *buf, size_t len, unsigned nbits)
{
bool notify = false;
+ int s;
+
+ KASSERT(cold);
- KASSERT(E->stage == ENTROPY_COLD);
+ /*
+ * We're early at boot before multithreading and multi-CPU
+ * operation, and we don't have softints yet to defer
+ * processing from interrupt context, so we have to enter the
+ * samples directly into the global pool. But interrupts may
+ * be enabled, and we enter this path from interrupt context,
+ * so block interrupts until we're done.
+ */
+ s = splhigh();
/* Enter it into the pool. */
entpool_enter(&E->pool, buf, len);
@@ -858,20 +884,29 @@ entropy_enter_early(const void *buf, siz
* Decide whether to notify reseed -- we will do so if either:
* (a) we transition from partial entropy to full entropy, or
* (b) we get a batch of full entropy all at once.
+ * We don't count timing samples because we assume, while cold,
+ * there's not likely to be much jitter yet.
*/
- notify |= (E->needed && E->needed <= nbits);
- notify |= (nbits >= ENTROPY_CAPACITY*NBBY);
+ notify |= (E->bitsneeded && E->bitsneeded <= nbits);
+ notify |= (nbits >= MINENTROPYBITS);
- /* Subtract from the needed count and notify if appropriate. */
- E->needed -= MIN(E->needed, nbits);
+ /*
+ * Subtract from the needed count and notify if appropriate.
+ * We don't count samples here because entropy_timer might
+ * still be returning zero at this point if there's no CPU
+ * cycle counter.
+ */
+ E->bitsneeded -= MIN(E->bitsneeded, nbits);
if (notify) {
entropy_notify();
entropy_immediate_evcnt.ev_count++;
}
+
+ splx(s);
}
/*
- * entropy_enter(buf, len, nbits)
+ * entropy_enter(buf, len, nbits, count)
*
* Enter len bytes of data from buf into the system's entropy
* pool, stirring as necessary when the internal buffer fills up.
@@ -879,11 +914,11 @@ entropy_enter_early(const void *buf, siz
* process that led to this sample.
*/
static void
-entropy_enter(const void *buf, size_t len, unsigned nbits)
+entropy_enter(const void *buf, size_t len, unsigned nbits, bool count)
{
struct entropy_cpu_lock lock;
struct entropy_cpu *ec;
- unsigned pending;
+ unsigned bitspending, samplespending;
int bound;
KASSERTMSG(!cpu_intr_p(),
@@ -891,8 +926,11 @@ entropy_enter(const void *buf, size_t le
KASSERTMSG(howmany(nbits, NBBY) <= len,
"impossible entropy rate: %u bits in %zu-byte string", nbits, len);
- /* If it's too early after boot, just use entropy_enter_early. */
- if (__predict_false(E->stage == ENTROPY_COLD)) {
+ /*
+ * If we're still cold, just use entropy_enter_early to put
+ * samples directly into the global pool.
+ */
+ if (__predict_false(cold)) {
entropy_enter_early(buf, len, nbits);
return;
}
@@ -908,23 +946,32 @@ entropy_enter(const void *buf, size_t le
/*
* With the per-CPU state locked, enter into the per-CPU pool
* and count up what we can add.
+ *
+ * We don't count samples while cold because entropy_timer
+ * might still be returning zero if there's no CPU cycle
+ * counter.
*/
ec = entropy_cpu_get(&lock);
entpool_enter(ec->ec_pool, buf, len);
- pending = ec->ec_pending;
- pending += MIN(ENTROPY_CAPACITY*NBBY - pending, nbits);
- atomic_store_relaxed(&ec->ec_pending, pending);
+ bitspending = ec->ec_bitspending;
+ bitspending += MIN(MINENTROPYBITS - bitspending, nbits);
+ atomic_store_relaxed(&ec->ec_bitspending, bitspending);
+ samplespending = ec->ec_samplespending;
+ if (__predict_true(count)) {
+ samplespending += MIN(MINSAMPLES - samplespending, 1);
+ atomic_store_relaxed(&ec->ec_samplespending, samplespending);
+ }
entropy_cpu_put(&lock, ec);
/* Consolidate globally if appropriate based on what we added. */
- if (pending)
+ if (bitspending > 0 || samplespending >= MINSAMPLES)
entropy_account_cpu(ec);
curlwp_bindx(bound);
}
/*
- * entropy_enter_intr(buf, len, nbits)
+ * entropy_enter_intr(buf, len, nbits, count)
*
* Enter up to len bytes of data from buf into the system's
* entropy pool without stirring. nbits is a lower bound on the
@@ -935,28 +982,40 @@ entropy_enter(const void *buf, size_t le
* instance. Schedule a softint to stir the entropy pool if
* needed. Return true if used fully, false if truncated at all.
*
- * Using this in thread context will work, but you might as well
- * use entropy_enter in that case.
+ * Using this in thread or softint context with no spin locks held
+ * will work, but you might as well use entropy_enter in that
+ * case.
*/
static bool
-entropy_enter_intr(const void *buf, size_t len, unsigned nbits)
+entropy_enter_intr(const void *buf, size_t len, unsigned nbits, bool count)
{
struct entropy_cpu *ec;
bool fullyused = false;
- uint32_t pending;
- void *sih;
+ uint32_t bitspending, samplespending;
+ int s;
- KASSERT(cpu_intr_p());
KASSERTMSG(howmany(nbits, NBBY) <= len,
"impossible entropy rate: %u bits in %zu-byte string", nbits, len);
- /* If it's too early after boot, just use entropy_enter_early. */
- if (__predict_false(E->stage == ENTROPY_COLD)) {
+ /*
+ * If we're still cold, just use entropy_enter_early to put
+ * samples directly into the global pool.
+ */
+ if (__predict_false(cold)) {
entropy_enter_early(buf, len, nbits);
return true;
}
/*
+ * In case we were called in thread or interrupt context with
+ * interrupts unblocked, block soft interrupts up to
+ * IPL_SOFTSERIAL. This way logic that is safe in interrupt
+ * context or under a spin lock is also safe in less
+ * restrictive contexts.
+ */
+ s = splsoftserial();
+
+ /*
* Acquire the per-CPU state. If someone is in the middle of
* using it, drop the sample. Otherwise, take the lock so that
* higher-priority interrupts will drop their samples.
@@ -974,33 +1033,42 @@ entropy_enter_intr(const void *buf, size
* truncated, schedule a softint to stir the pool and stop.
*/
if (!entpool_enter_nostir(ec->ec_pool, buf, len)) {
- sih = atomic_load_relaxed(&entropy_sih);
- if (__predict_true(sih != NULL))
- softint_schedule(sih);
+ if (__predict_true(!cold))
+ softint_schedule(entropy_sih);
ec->ec_evcnt->intrtrunc.ev_count++;
goto out1;
}
fullyused = true;
- /* Count up what we can contribute. */
- pending = ec->ec_pending;
- pending += MIN(ENTROPY_CAPACITY*NBBY - pending, nbits);
- atomic_store_relaxed(&ec->ec_pending, pending);
+ /*
+ * Count up what we can contribute.
+ *
+ * We don't count samples while cold because entropy_timer
+ * might still be returning zero if there's no CPU cycle
+ * counter.
+ */
+ bitspending = ec->ec_bitspending;
+ bitspending += MIN(MINENTROPYBITS - bitspending, nbits);
+ atomic_store_relaxed(&ec->ec_bitspending, bitspending);
+ if (__predict_true(count)) {
+ samplespending = ec->ec_samplespending;
+ samplespending += MIN(MINSAMPLES - samplespending, 1);
+ atomic_store_relaxed(&ec->ec_samplespending, samplespending);
+ }
/* Schedule a softint if we added anything and it matters. */
- if (__predict_false((atomic_load_relaxed(&E->needed) != 0) ||
+ if (__predict_false(atomic_load_relaxed(&E->bitsneeded) ||
atomic_load_relaxed(&entropy_depletion)) &&
- nbits != 0) {
- sih = atomic_load_relaxed(&entropy_sih);
- if (__predict_true(sih != NULL))
- softint_schedule(sih);
- }
+ (nbits != 0 || count) &&
+ __predict_true(!cold))
+ softint_schedule(entropy_sih);
out1: /* Release the per-CPU state. */
KASSERT(ec->ec_locked);
__insn_barrier();
ec->ec_locked = false;
out0: percpu_putref(entropy_percpu);
+ splx(s);
return fullyused;
}
@@ -1018,7 +1086,7 @@ entropy_softintr(void *cookie)
{
struct entropy_cpu_lock lock;
struct entropy_cpu *ec;
- unsigned pending;
+ unsigned bitspending, samplespending;
/*
* With the per-CPU state locked, stir the pool if necessary
@@ -1028,11 +1096,12 @@ entropy_softintr(void *cookie)
ec = entropy_cpu_get(&lock);
ec->ec_evcnt->softint.ev_count++;
entpool_stir(ec->ec_pool);
- pending = ec->ec_pending;
+ bitspending = ec->ec_bitspending;
+ samplespending = ec->ec_samplespending;
entropy_cpu_put(&lock, ec);
/* Consolidate globally if appropriate based on what we added. */
- if (pending)
+ if (bitspending > 0 || samplespending >= MINSAMPLES)
entropy_account_cpu(ec);
}
@@ -1046,13 +1115,17 @@ entropy_thread(void *cookie)
{
bool consolidate;
+#ifndef _RUMPKERNEL /* XXX rump starts threads before cold */
+ KASSERT(!cold);
+#endif
+
for (;;) {
/*
* Wait until there's full entropy somewhere among the
* CPUs, as confirmed at most once per minute, or
* someone wants to consolidate.
*/
- if (entropy_pending() >= ENTROPY_CAPACITY*NBBY) {
+ if (entropy_pending()) {
consolidate = true;
} else {
mutex_enter(&E->lock);
@@ -1073,29 +1146,41 @@ entropy_thread(void *cookie)
}
}
+struct entropy_pending_count {
+ uint32_t bitspending;
+ uint32_t samplespending;
+};
+
/*
* entropy_pending()
*
- * Count up the amount of entropy pending on other CPUs.
+ * True if enough bits or samples are pending on other CPUs to
+ * warrant consolidation.
*/
-static uint32_t
+static bool
entropy_pending(void)
{
- uint32_t pending = 0;
+ struct entropy_pending_count count = { 0, 0 }, *C = &count;
- percpu_foreach(entropy_percpu, &entropy_pending_cpu, &pending);
- return pending;
+ percpu_foreach(entropy_percpu, &entropy_pending_cpu, C);
+ return C->bitspending >= MINENTROPYBITS ||
+ C->samplespending >= MINSAMPLES;
}
static void
entropy_pending_cpu(void *ptr, void *cookie, struct cpu_info *ci)
{
struct entropy_cpu *ec = ptr;
- uint32_t *pendingp = cookie;
- uint32_t cpu_pending;
-
- cpu_pending = atomic_load_relaxed(&ec->ec_pending);
- *pendingp += MIN(ENTROPY_CAPACITY*NBBY - *pendingp, cpu_pending);
+ struct entropy_pending_count *C = cookie;
+ uint32_t cpu_bitspending;
+ uint32_t cpu_samplespending;
+
+ cpu_bitspending = atomic_load_relaxed(&ec->ec_bitspending);
+ cpu_samplespending = atomic_load_relaxed(&ec->ec_samplespending);
+ C->bitspending += MIN(MINENTROPYBITS - C->bitspending,
+ cpu_bitspending);
+ C->samplespending += MIN(MINSAMPLES - C->samplespending,
+ cpu_samplespending);
}
/*
@@ -1111,9 +1196,12 @@ entropy_do_consolidate(void)
static struct timeval lasttime; /* serialized by E->lock */
struct entpool pool;
uint8_t buf[ENTPOOL_CAPACITY];
- unsigned diff;
+ unsigned bitsdiff, samplesdiff;
uint64_t ticket;
+ KASSERT(!cold);
+ ASSERT_SLEEPABLE();
+
/* Gather entropy on all CPUs into a temporary pool. */
memset(&pool, 0, sizeof pool);
ticket = xc_broadcast(0, &entropy_consolidate_xc, &pool, NULL);
@@ -1134,10 +1222,10 @@ entropy_do_consolidate(void)
explicit_memset(&pool, 0, sizeof pool);
/* Count the entropy that was gathered. */
- diff = MIN(E->needed, E->pending);
- atomic_store_relaxed(&E->needed, E->needed - diff);
- E->pending -= diff;
- if (__predict_false(E->needed > 0)) {
+ bitsdiff = MIN(E->bitsneeded, E->bitspending);
+ atomic_store_relaxed(&E->bitsneeded, E->bitsneeded - bitsdiff);
+ E->bitspending -= bitsdiff;
+ if (__predict_false(E->bitsneeded > 0) && bitsdiff != 0) {
if ((boothowto & AB_DEBUG) != 0 &&
ratecheck(&lasttime, &interval)) {
printf("WARNING:"
@@ -1145,6 +1233,11 @@ entropy_do_consolidate(void)
}
}
+ samplesdiff = MIN(E->samplesneeded, E->samplespending);
+ atomic_store_relaxed(&E->samplesneeded,
+ E->samplesneeded - samplesdiff);
+ E->samplespending -= samplesdiff;
+
/* Advance the epoch and notify waiters. */
entropy_notify();
@@ -1179,7 +1272,8 @@ entropy_consolidate_xc(void *vpool, void
ec = entropy_cpu_get(&lock);
extra[i++] = entropy_timer();
entpool_extract(ec->ec_pool, buf, sizeof buf);
- atomic_store_relaxed(&ec->ec_pending, 0);
+ atomic_store_relaxed(&ec->ec_bitspending, 0);
+ atomic_store_relaxed(&ec->ec_samplespending, 0);
extra[i++] = entropy_timer();
entropy_cpu_put(&lock, ec);
extra[i++] = entropy_timer();
@@ -1205,27 +1299,37 @@ entropy_consolidate_xc(void *vpool, void
* Caller just contributed entropy to the global pool. Advance
* the entropy epoch and notify waiters.
*
- * Caller must hold the global entropy lock. Except for the
- * `sysctl -w kern.entropy.consolidate=1` trigger, the caller must
- * have just have transitioned from partial entropy to full
- * entropy -- E->needed should be zero now.
+ * Caller must hold the global entropy lock.
*/
static void
entropy_notify(void)
{
static const struct timeval interval = {.tv_sec = 60, .tv_usec = 0};
static struct timeval lasttime; /* serialized by E->lock */
+ static bool ready = false, besteffort = false;
unsigned epoch;
- KASSERT(E->stage == ENTROPY_COLD || mutex_owned(&E->lock));
+ KASSERT(__predict_false(cold) || mutex_owned(&E->lock));
/*
* If this is the first time, print a message to the console
* that we're ready so operators can compare it to the timing
* of other events.
- */
- if (__predict_false(E->epoch == (unsigned)-1) && E->needed == 0)
- printf("entropy: ready\n");
+ *
+ * If we didn't get full entropy from reliable sources, report
+ * instead that we are running on fumes with best effort. (If
+ * we ever do get full entropy after that, print the ready
+ * message once.)
+ */
+ if (__predict_false(!ready)) {
+ if (E->bitsneeded == 0) {
+ printf("entropy: ready\n");
+ ready = true;
+ } else if (E->samplesneeded == 0 && !besteffort) {
+ printf("entropy: best effort\n");
+ besteffort = true;
+ }
+ }
/* Set the epoch; roll over from UINTMAX-1 to 1. */
if (__predict_true(!atomic_load_relaxed(&entropy_depletion)) ||
@@ -1238,7 +1342,7 @@ entropy_notify(void)
KASSERT(E->epoch != (unsigned)-1);
/* Notify waiters. */
- if (E->stage >= ENTROPY_WARM) {
+ if (__predict_true(!cold)) {
cv_broadcast(&E->cv);
selnotify(&E->selq, POLLIN|POLLRDNORM, NOTE_SUBMIT);
}
@@ -1264,7 +1368,8 @@ entropy_consolidate(void)
uint64_t ticket;
int error;
- KASSERT(E->stage == ENTROPY_HOT);
+ KASSERT(!cold);
+ ASSERT_SLEEPABLE();
mutex_enter(&E->lock);
ticket = entropy_consolidate_evcnt.ev_count;
@@ -1294,8 +1399,6 @@ sysctl_entropy_consolidate(SYSCTLFN_ARGS
int arg = 0;
int error;
- KASSERT(E->stage == ENTROPY_HOT);
-
node.sysctl_data = &arg;
error = sysctl_lookup(SYSCTLFN_CALL(&node));
if (error || newp == NULL)
@@ -1320,8 +1423,6 @@ sysctl_entropy_gather(SYSCTLFN_ARGS)
int arg = 0;
int error;
- KASSERT(E->stage == ENTROPY_HOT);
-
node.sysctl_data = &arg;
error = sysctl_lookup(SYSCTLFN_CALL(&node));
if (error || newp == NULL)
@@ -1348,6 +1449,9 @@ sysctl_entropy_gather(SYSCTLFN_ARGS)
* provide backtracking resistance -- it must be combined with a
* PRNG/DRBG that does.
*
+ * This may be used very early at boot, before even entropy_init
+ * has been called.
+ *
* You generally shouldn't use this directly -- use cprng(9)
* instead.
*
@@ -1364,39 +1468,56 @@ sysctl_entropy_gather(SYSCTLFN_ARGS)
* EINTR/ERESTART No entropy, ENTROPY_SIG set, and interrupted.
*
* If ENTROPY_WAIT is set, allowed only in thread context. If
- * ENTROPY_WAIT is not set, allowed also in softint context.
- * Forbidden in hard interrupt context.
+ * ENTROPY_WAIT is not set, allowed also in softint context -- may
+ * sleep on an adaptive lock up to IPL_SOFTSERIAL. Forbidden in
+ * hard interrupt context.
*/
int
entropy_extract(void *buf, size_t len, int flags)
{
static const struct timeval interval = {.tv_sec = 60, .tv_usec = 0};
static struct timeval lasttime; /* serialized by E->lock */
- int error;
+ bool printed = false;
+ int s = -1/*XXXGCC*/, error;
if (ISSET(flags, ENTROPY_WAIT)) {
ASSERT_SLEEPABLE();
- KASSERTMSG(E->stage >= ENTROPY_WARM,
- "can't wait for entropy until warm");
+ KASSERT(!cold);
}
/* Refuse to operate in interrupt context. */
KASSERT(!cpu_intr_p());
- /* Acquire the global lock to get at the global pool. */
- if (E->stage >= ENTROPY_WARM)
+ /*
+ * If we're cold, we are only contending with interrupts on the
+ * current CPU, so block them. Otherwise, we are _not_
+ * contending with interrupts on the current CPU, but we are
+ * contending with other threads, to exclude them with a mutex.
+ */
+ if (__predict_false(cold))
+ s = splhigh();
+ else
mutex_enter(&E->lock);
/* Wait until there is enough entropy in the system. */
error = 0;
- while (E->needed) {
+ if (E->bitsneeded > 0 && E->samplesneeded == 0) {
+ /*
+ * We don't have full entropy from reliable sources,
+ * but we gathered a plausible number of samples from
+ * other sources such as timers. Try asking for more
+ * from any sources we can, but don't worry if it
+ * fails -- best effort.
+ */
+ (void)entropy_request(ENTROPY_CAPACITY, flags);
+ } else while (E->bitsneeded > 0 && E->samplesneeded > 0) {
/* Ask for more, synchronously if possible. */
error = entropy_request(len, flags);
if (error)
break;
/* If we got enough, we're done. */
- if (E->needed == 0) {
+ if (E->bitsneeded == 0 || E->samplesneeded == 0) {
KASSERT(error == 0);
break;
}
@@ -1408,16 +1529,19 @@ entropy_extract(void *buf, size_t len, i
}
/* Wait for some entropy to come in and try again. */
- KASSERT(E->stage >= ENTROPY_WARM);
- printf("entropy: pid %d (%s) blocking due to lack of entropy\n",
- curproc->p_pid, curproc->p_comm);
+ KASSERT(!cold);
+ if (!printed) {
+ printf("entropy: pid %d (%s) waiting for entropy(7)\n",
+ curproc->p_pid, curproc->p_comm);
+ printed = true;
+ }
if (ISSET(flags, ENTROPY_SIG)) {
- error = cv_wait_sig(&E->cv, &E->lock);
- if (error)
+ error = cv_timedwait_sig(&E->cv, &E->lock, hz);
+ if (error && error != EWOULDBLOCK)
break;
} else {
- cv_wait(&E->cv, &E->lock);
+ cv_timedwait(&E->cv, &E->lock, hz);
}
}
@@ -1432,7 +1556,7 @@ entropy_extract(void *buf, size_t len, i
}
/*
- * Report a warning if we have never yet reached full entropy.
+ * Report a warning if we haven't yet reached full entropy.
* This is the only case where we consider entropy to be
* `depleted' without kern.entropy.depletion enabled -- when we
* only have partial entropy, an adversary may be able to
@@ -1440,13 +1564,22 @@ entropy_extract(void *buf, size_t len, i
* possibilities; the output then enables them to confirm a
* guess, reducing its entropy from the adversary's perspective
* to zero.
+ *
+ * This should only happen if the operator has chosen to
+ * consolidate, either through sysctl kern.entropy.consolidate
+ * or by writing less than full entropy to /dev/random as root
+ * (which /dev/random promises will immediately affect
+ * subsequent output, for better or worse).
*/
- if (__predict_false(E->epoch == (unsigned)-1)) {
- if (ratecheck(&lasttime, &interval))
+ if (E->bitsneeded > 0 && E->samplesneeded > 0) {
+ if (__predict_false(E->epoch == (unsigned)-1) &&
+ ratecheck(&lasttime, &interval)) {
printf("WARNING:"
" system needs entropy for security;"
" see entropy(7)\n");
- atomic_store_relaxed(&E->needed, ENTROPY_CAPACITY*NBBY);
+ }
+ atomic_store_relaxed(&E->bitsneeded, MINENTROPYBITS);
+ atomic_store_relaxed(&E->samplesneeded, MINSAMPLES);
}
/* Extract data from the pool, and `deplete' if we're doing that. */
@@ -1454,14 +1587,21 @@ entropy_extract(void *buf, size_t len, i
if (__predict_false(atomic_load_relaxed(&entropy_depletion)) &&
error == 0) {
unsigned cost = MIN(len, ENTROPY_CAPACITY)*NBBY;
+ unsigned bitsneeded = E->bitsneeded;
+ unsigned samplesneeded = E->samplesneeded;
+
+ bitsneeded += MIN(MINENTROPYBITS - bitsneeded, cost);
+ samplesneeded += MIN(MINSAMPLES - samplesneeded, cost);
- atomic_store_relaxed(&E->needed,
- E->needed + MIN(ENTROPY_CAPACITY*NBBY - E->needed, cost));
+ atomic_store_relaxed(&E->bitsneeded, bitsneeded);
+ atomic_store_relaxed(&E->samplesneeded, samplesneeded);
entropy_deplete_evcnt.ev_count++;
}
out: /* Release the global lock and return the error. */
- if (E->stage >= ENTROPY_WARM)
+ if (__predict_false(cold))
+ splx(s);
+ else
mutex_exit(&E->lock);
return error;
}
@@ -1477,7 +1617,7 @@ entropy_poll(int events)
{
int revents = 0;
- KASSERT(E->stage >= ENTROPY_WARM);
+ KASSERT(!cold);
/* Always ready for writing. */
revents |= events & (POLLOUT|POLLWRNORM);
@@ -1491,7 +1631,8 @@ entropy_poll(int events)
* If we have reached full entropy and we're not depleting
* entropy, we are forever ready.
*/
- if (__predict_true(atomic_load_relaxed(&E->needed) == 0) &&
+ if (__predict_true(atomic_load_relaxed(&E->bitsneeded) == 0 ||
+ atomic_load_relaxed(&E->samplesneeded) == 0) &&
__predict_true(!atomic_load_relaxed(&entropy_depletion)))
return revents | events;
@@ -1500,7 +1641,7 @@ entropy_poll(int events)
* we don't, we're ready; if we do, add ourselves to the queue.
*/
mutex_enter(&E->lock);
- if (E->needed == 0)
+ if (E->bitsneeded == 0 || E->samplesneeded == 0)
revents |= events;
else
selrecord(curlwp, &E->selq);
@@ -1519,7 +1660,7 @@ static void
filt_entropy_read_detach(struct knote *kn)
{
- KASSERT(E->stage >= ENTROPY_WARM);
+ KASSERT(!cold);
mutex_enter(&E->lock);
selremove_knote(&E->selq, kn);
@@ -1538,7 +1679,7 @@ filt_entropy_read_event(struct knote *kn
{
int ret;
- KASSERT(E->stage >= ENTROPY_WARM);
+ KASSERT(!cold);
/* Acquire the lock, if caller is outside entropy subsystem. */
if (hint == NOTE_SUBMIT)
@@ -1550,7 +1691,7 @@ filt_entropy_read_event(struct knote *kn
* If we still need entropy, can't read anything; if not, can
* read arbitrarily much.
*/
- if (E->needed != 0) {
+ if (E->bitsneeded != 0 && E->samplesneeded != 0) {
ret = 0;
} else {
if (atomic_load_relaxed(&entropy_depletion))
@@ -1587,7 +1728,7 @@ int
entropy_kqfilter(struct knote *kn)
{
- KASSERT(E->stage >= ENTROPY_WARM);
+ KASSERT(!cold);
switch (kn->kn_filter) {
case EVFILT_READ:
@@ -1663,15 +1804,15 @@ rnd_attach_source(struct krndsource *rs,
rs->total = 0;
rs->type = type;
rs->flags = flags;
- if (E->stage >= ENTROPY_WARM)
+ if (entropy_percpu != NULL)
rs->state = percpu_alloc(sizeof(struct rndsource_cpu));
extra[i++] = entropy_timer();
/* Wire it into the global list of random sources. */
- if (E->stage >= ENTROPY_WARM)
+ if (__predict_true(!cold))
mutex_enter(&E->lock);
LIST_INSERT_HEAD(&E->sources, rs, list);
- if (E->stage >= ENTROPY_WARM)
+ if (__predict_true(!cold))
mutex_exit(&E->lock);
extra[i++] = entropy_timer();
@@ -1682,7 +1823,7 @@ rnd_attach_source(struct krndsource *rs,
/* Mix the extra into the pool. */
KASSERT(i == __arraycount(extra));
- entropy_enter(extra, sizeof extra, 0);
+ entropy_enter(extra, sizeof extra, 0, /*count*/__predict_true(!cold));
explicit_memset(extra, 0, sizeof extra);
}
@@ -1700,7 +1841,7 @@ rnd_detach_source(struct krndsource *rs)
* If we're cold (shouldn't happen, but hey), just remove it
* from the list -- there's nothing allocated.
*/
- if (E->stage == ENTROPY_COLD) {
+ if (__predict_false(cold) && entropy_percpu == NULL) {
LIST_REMOVE(rs, list);
return;
}
@@ -1727,6 +1868,8 @@ rnd_detach_source(struct krndsource *rs)
* rnd_unlock_sources even while the caller releases the global
* entropy lock.
*
+ * May be called very early at boot, before entropy_init.
+ *
* If flags & ENTROPY_WAIT, wait for concurrent access to finish.
* If flags & ENTROPY_SIG, allow interruption by signal.
*/
@@ -1735,10 +1878,11 @@ rnd_lock_sources(int flags)
{
int error;
- KASSERT(E->stage == ENTROPY_COLD || mutex_owned(&E->lock));
+ KASSERT(__predict_false(cold) || mutex_owned(&E->lock));
+ KASSERT(!cpu_intr_p());
while (E->sourcelock) {
- KASSERT(E->stage >= ENTROPY_WARM);
+ KASSERT(!cold);
if (!ISSET(flags, ENTROPY_WAIT))
return EWOULDBLOCK;
if (ISSET(flags, ENTROPY_SIG)) {
@@ -1759,17 +1903,20 @@ rnd_lock_sources(int flags)
*
* Unlock the list of sources after rnd_lock_sources. Caller must
* hold the global entropy lock.
+ *
+ * May be called very early at boot, before entropy_init.
*/
static void
rnd_unlock_sources(void)
{
- KASSERT(E->stage == ENTROPY_COLD || mutex_owned(&E->lock));
+ KASSERT(__predict_false(cold) || mutex_owned(&E->lock));
+ KASSERT(!cpu_intr_p());
KASSERTMSG(E->sourcelock == curlwp, "lwp %p releasing lock held by %p",
curlwp, E->sourcelock);
E->sourcelock = NULL;
- if (E->stage >= ENTROPY_WARM)
+ if (__predict_true(!cold))
cv_signal(&E->sourcelock_cv);
}
@@ -1778,6 +1925,8 @@ rnd_unlock_sources(void)
*
* True if we hold the list of rndsources locked, for diagnostic
* assertions.
+ *
+ * May be called very early at boot, before entropy_init.
*/
static bool __diagused
rnd_sources_locked(void)
@@ -1793,6 +1942,8 @@ rnd_sources_locked(void)
* OK if we overdo it. Caller must hold the global entropy lock;
* will release and re-acquire it.
*
+ * May be called very early at boot, before entropy_init.
+ *
* If flags & ENTROPY_WAIT, wait for concurrent access to finish.
* If flags & ENTROPY_SIG, allow interruption by signal.
*/
@@ -1802,8 +1953,9 @@ entropy_request(size_t nbytes, int flags
struct krndsource *rs;
int error;
- KASSERT(E->stage == ENTROPY_COLD || mutex_owned(&E->lock));
- if (flags & ENTROPY_WAIT)
+ KASSERT(__predict_false(cold) || mutex_owned(&E->lock));
+ KASSERT(!cpu_intr_p());
+ if ((flags & ENTROPY_WAIT) != 0 && __predict_false(!cold))
ASSERT_SLEEPABLE();
/*
@@ -1833,10 +1985,10 @@ entropy_request(size_t nbytes, int flags
continue;
/* Drop the lock while we call the callback. */
- if (E->stage >= ENTROPY_WARM)
+ if (__predict_true(!cold))
mutex_exit(&E->lock);
(*rs->get)(nbytes, rs->getarg);
- if (E->stage >= ENTROPY_WARM)
+ if (__predict_true(!cold))
mutex_enter(&E->lock);
}
@@ -1845,35 +1997,98 @@ entropy_request(size_t nbytes, int flags
return 0;
}
+static inline uint32_t
+rnd_delta_estimate(rnd_delta_t *d, uint32_t v, int32_t delta)
+{
+ int32_t delta2, delta3;
+
+ /*
+ * Calculate the second and third order differentials
+ */
+ delta2 = d->dx - delta;
+ if (delta2 < 0)
+ delta2 = -delta2; /* XXX arithmetic overflow */
+
+ delta3 = d->d2x - delta2;
+ if (delta3 < 0)
+ delta3 = -delta3; /* XXX arithmetic overflow */
+
+ d->x = v;
+ d->dx = delta;
+ d->d2x = delta2;
+
+ /*
+ * If any delta is 0, we got no entropy. If all are non-zero, we
+ * might have something.
+ */
+ if (delta == 0 || delta2 == 0 || delta3 == 0)
+ return 0;
+
+ return 1;
+}
+
+static inline uint32_t
+rnd_dt_estimate(struct krndsource *rs, uint32_t t)
+{
+ int32_t delta;
+ uint32_t ret;
+ rnd_delta_t *d;
+ struct rndsource_cpu *rc;
+
+ rc = percpu_getref(rs->state);
+ d = &rc->rc_timedelta;
+
+ if (t < d->x) {
+ delta = UINT32_MAX - d->x + t;
+ } else {
+ delta = d->x - t;
+ }
+
+ if (delta < 0) {
+ delta = -delta; /* XXX arithmetic overflow */
+ }
+
+ ret = rnd_delta_estimate(d, t, delta);
+
+ KASSERT(d->x == t);
+ KASSERT(d->dx == delta);
+ percpu_putref(rs->state);
+ return ret;
+}
+
/*
* rnd_add_uint32(rs, value)
*
* Enter 32 bits of data from an entropy source into the pool.
*
- * If rs is NULL, may not be called from interrupt context.
+ * May be called from any context or with spin locks held, but may
+ * drop data.
*
- * If rs is non-NULL, may be called from any context. May drop
- * data if called from interrupt context.
+ * This is meant for cheaply taking samples from devices that
+ * aren't designed to be hardware random number generators.
*/
void
rnd_add_uint32(struct krndsource *rs, uint32_t value)
{
+ bool intr_p = true;
- rnd_add_data(rs, &value, sizeof value, 0);
+ rnd_add_data_internal(rs, &value, sizeof value, 0, intr_p);
}
void
_rnd_add_uint32(struct krndsource *rs, uint32_t value)
{
+ bool intr_p = true;
- rnd_add_data(rs, &value, sizeof value, 0);
+ rnd_add_data_internal(rs, &value, sizeof value, 0, intr_p);
}
void
_rnd_add_uint64(struct krndsource *rs, uint64_t value)
{
+ bool intr_p = true;
- rnd_add_data(rs, &value, sizeof value, 0);
+ rnd_add_data_internal(rs, &value, sizeof value, 0, intr_p);
}
/*
@@ -1884,32 +2099,87 @@ _rnd_add_uint64(struct krndsource *rs, u
* the data has. If RND_FLAG_NO_ESTIMATE, we ignore the driver's
* estimate and treat it as zero.
*
- * If rs is NULL, may not be called from interrupt context.
+ * rs MAY but SHOULD NOT be NULL. If rs is NULL, MUST NOT be
+ * called from interrupt context or with spin locks held.
*
- * If rs is non-NULL, may be called from any context. May drop
- * data if called from interrupt context.
+ * If rs is non-NULL, MAY but SHOULD NOT be called from interrupt
+ * context, in which case act like rnd_add_data_intr -- if the
+ * sample buffer is full, schedule a softint and drop any
+ * additional data on the floor. (This may change later once we
+ * fix drivers that still call this from interrupt context to use
+ * rnd_add_data_intr instead.) MUST NOT be called with spin locks
+ * held if not in hard interrupt context -- i.e., MUST NOT be
+ * called in thread context or softint context with spin locks
+ * held.
*/
void
rnd_add_data(struct krndsource *rs, const void *buf, uint32_t len,
uint32_t entropybits)
{
- uint32_t extra;
- uint32_t flags;
+ bool intr_p = cpu_intr_p(); /* XXX make this unconditionally false */
- KASSERTMSG(howmany(entropybits, NBBY) <= len,
- "%s: impossible entropy rate:"
- " %"PRIu32" bits in %"PRIu32"-byte string",
- rs ? rs->name : "(anonymous)", entropybits, len);
-
- /* If there's no rndsource, just enter the data and time now. */
+ /*
+ * Weird legacy exception that we should rip out and replace by
+ * creating new rndsources to attribute entropy to the callers:
+ * If there's no rndsource, just enter the data and time now.
+ */
if (rs == NULL) {
- entropy_enter(buf, len, entropybits);
+ uint32_t extra;
+
+ KASSERT(!intr_p);
+ KASSERTMSG(howmany(entropybits, NBBY) <= len,
+ "%s: impossible entropy rate:"
+ " %"PRIu32" bits in %"PRIu32"-byte string",
+ rs ? rs->name : "(anonymous)", entropybits, len);
+ entropy_enter(buf, len, entropybits, /*count*/false);
extra = entropy_timer();
- entropy_enter(&extra, sizeof extra, 0);
+ entropy_enter(&extra, sizeof extra, 0, /*count*/false);
explicit_memset(&extra, 0, sizeof extra);
return;
}
+ rnd_add_data_internal(rs, buf, len, entropybits, intr_p);
+}
+
+/*
+ * rnd_add_data_intr(rs, buf, len, entropybits)
+ *
+ * Try to enter data from an entropy source into the pool, with a
+ * driver's estimate of how much entropy the physical source of
+ * the data has. If RND_FLAG_NO_ESTIMATE, we ignore the driver's
+ * estimate and treat it as zero. If the sample buffer is full,
+ * schedule a softint and drop any additional data on the floor.
+ */
+void
+rnd_add_data_intr(struct krndsource *rs, const void *buf, uint32_t len,
+ uint32_t entropybits)
+{
+ bool intr_p = true;
+
+ rnd_add_data_internal(rs, buf, len, entropybits, intr_p);
+}
+
+/*
+ * rnd_add_data_internal(rs, buf, len, entropybits, intr_p)
+ *
+ * Internal subroutine to decide whether or not to enter data or
+ * timing for a particular rndsource, and if so, to enter it.
+ *
+ * intr_p is true for callers from interrupt context or spin locks
+ * held, and false for callers from thread or soft interrupt
+ * context and no spin locks held.
+ */
+static void
+rnd_add_data_internal(struct krndsource *rs, const void *buf, uint32_t len,
+ uint32_t entropybits, bool intr_p)
+{
+ uint32_t flags;
+
+ KASSERTMSG(howmany(entropybits, NBBY) <= len,
+ "%s: impossible entropy rate:"
+ " %"PRIu32" bits in %"PRIu32"-byte string",
+ rs ? rs->name : "(anonymous)", entropybits, len);
+
/*
* Hold up the reset xcall before it zeroes the entropy counts
* on this CPU or globally. Otherwise, we might leave some
@@ -1937,15 +2207,28 @@ rnd_add_data(struct krndsource *rs, cons
entropybits = 0;
/* If we are collecting data, enter them. */
- if (ISSET(flags, RND_FLAG_COLLECT_VALUE))
- rnd_add_data_1(rs, buf, len, entropybits,
- RND_FLAG_COLLECT_VALUE);
+ if (ISSET(flags, RND_FLAG_COLLECT_VALUE)) {
+ rnd_add_data_1(rs, buf, len, entropybits, /*count*/false,
+ RND_FLAG_COLLECT_VALUE, intr_p);
+ }
/* If we are collecting timings, enter one. */
if (ISSET(flags, RND_FLAG_COLLECT_TIME)) {
+ uint32_t extra;
+ bool count;
+
+ /* Sample a timer. */
extra = entropy_timer();
- rnd_add_data_1(rs, &extra, sizeof extra, 0,
- RND_FLAG_COLLECT_TIME);
+
+ /* If asked, do entropy estimation on the time. */
+ if ((flags & (RND_FLAG_ESTIMATE_TIME|RND_FLAG_NO_ESTIMATE)) ==
+ RND_FLAG_ESTIMATE_TIME && __predict_true(!cold))
+ count = rnd_dt_estimate(rs, extra);
+ else
+ count = false;
+
+ rnd_add_data_1(rs, &extra, sizeof extra, 0, count,
+ RND_FLAG_COLLECT_TIME, intr_p);
}
out: /* Allow concurrent changes to flags to finish. */
@@ -1961,7 +2244,7 @@ add_sat(unsigned a, unsigned b)
}
/*
- * rnd_add_data_1(rs, buf, len, entropybits, flag)
+ * rnd_add_data_1(rs, buf, len, entropybits, count, flag)
*
* Internal subroutine to call either entropy_enter_intr, if we're
* in interrupt context, or entropy_enter if not, and to count the
@@ -1969,19 +2252,19 @@ add_sat(unsigned a, unsigned b)
*/
static void
rnd_add_data_1(struct krndsource *rs, const void *buf, uint32_t len,
- uint32_t entropybits, uint32_t flag)
+ uint32_t entropybits, bool count, uint32_t flag, bool intr_p)
{
bool fullyused;
/*
- * If we're in interrupt context, use entropy_enter_intr and
- * take note of whether it consumed the full sample; if not,
- * use entropy_enter, which always consumes the full sample.
+ * For the interrupt-like path, use entropy_enter_intr and take
+ * note of whether it consumed the full sample; otherwise, use
+ * entropy_enter, which always consumes the full sample.
*/
- if (curlwp && cpu_intr_p()) {
- fullyused = entropy_enter_intr(buf, len, entropybits);
+ if (intr_p) {
+ fullyused = entropy_enter_intr(buf, len, entropybits, count);
} else {
- entropy_enter(buf, len, entropybits);
+ entropy_enter(buf, len, entropybits, count);
fullyused = true;
}
@@ -1990,7 +2273,8 @@ rnd_add_data_1(struct krndsource *rs, co
* contributed from this source.
*/
if (fullyused) {
- if (__predict_false(E->stage == ENTROPY_COLD)) {
+ if (__predict_false(cold)) {
+ const int s = splhigh();
rs->total = add_sat(rs->total, entropybits);
switch (flag) {
case RND_FLAG_COLLECT_TIME:
@@ -2002,6 +2286,7 @@ rnd_add_data_1(struct krndsource *rs, co
add_sat(rs->value_delta.insamples, 1);
break;
}
+ splx(s);
} else {
struct rndsource_cpu *rc = percpu_getref(rs->state);
@@ -2048,7 +2333,7 @@ rndsource_entropybits(struct krndsource
{
unsigned nbits = rs->total;
- KASSERT(E->stage >= ENTROPY_WARM);
+ KASSERT(!cold);
KASSERT(rnd_sources_locked());
percpu_foreach(rs->state, rndsource_entropybits_cpu, &nbits);
return nbits;
@@ -2074,7 +2359,7 @@ static void
rndsource_to_user(struct krndsource *rs, rndsource_t *urs)
{
- KASSERT(E->stage >= ENTROPY_WARM);
+ KASSERT(!cold);
KASSERT(rnd_sources_locked());
/* Avoid kernel memory disclosure. */
@@ -2097,7 +2382,7 @@ static void
rndsource_to_user_est(struct krndsource *rs, rndsource_est_t *urse)
{
- KASSERT(E->stage >= ENTROPY_WARM);
+ KASSERT(!cold);
KASSERT(rnd_sources_locked());
/* Avoid kernel memory disclosure. */
@@ -2143,7 +2428,8 @@ entropy_reset_xc(void *arg1 __unused, vo
* enter a cycle count for fun.
*/
ec = entropy_cpu_get(&lock);
- ec->ec_pending = 0;
+ ec->ec_bitspending = 0;
+ ec->ec_samplespending = 0;
entpool_enter(ec->ec_pool, &extra, sizeof extra);
entropy_cpu_put(&lock, ec);
}
@@ -2160,7 +2446,7 @@ entropy_ioctl(unsigned long cmd, void *d
bool privileged;
int error;
- KASSERT(E->stage >= ENTROPY_WARM);
+ KASSERT(!cold);
/* Verify user's authorization to perform the ioctl. */
switch (cmd) {
@@ -2220,7 +2506,7 @@ entropy_ioctl(unsigned long cmd, void *d
uint32_t *countp = data;
mutex_enter(&E->lock);
- *countp = ENTROPY_CAPACITY*NBBY - E->needed;
+ *countp = MINENTROPYBITS - E->bitsneeded;
mutex_exit(&E->lock);
break;
@@ -2232,15 +2518,22 @@ entropy_ioctl(unsigned long cmd, void *d
/* parameters */
pstat->poolsize = ENTPOOL_SIZE/sizeof(uint32_t); /* words */
- pstat->threshold = ENTROPY_CAPACITY*1; /* bytes */
+ pstat->threshold = MINENTROPYBITS/NBBY; /* bytes */
pstat->maxentropy = ENTROPY_CAPACITY*NBBY; /* bits */
/* state */
pstat->added = 0; /* XXX total entropy_enter count */
- pstat->curentropy = ENTROPY_CAPACITY*NBBY - E->needed;
+ pstat->curentropy = MINENTROPYBITS - E->bitsneeded; /* bits */
pstat->removed = 0; /* XXX total entropy_extract count */
pstat->discarded = 0; /* XXX bits of entropy beyond capacity */
- pstat->generated = 0; /* XXX bits of data...fabricated? */
+
+ /*
+ * This used to be bits of data fabricated in some
+ * sense; we'll take it to mean number of samples,
+ * excluding the bits of entropy from HWRNG or seed.
+ */
+ pstat->generated = MINSAMPLES - E->samplesneeded;
+ pstat->generated -= MIN(pstat->generated, pstat->curentropy);
mutex_exit(&E->lock);
break;
@@ -2422,9 +2715,10 @@ entropy_ioctl(unsigned long cmd, void *d
if (reset) {
xc_broadcast(0, &entropy_reset_xc, NULL, NULL);
mutex_enter(&E->lock);
- E->pending = 0;
- atomic_store_relaxed(&E->needed,
- ENTROPY_CAPACITY*NBBY);
+ E->bitspending = 0;
+ E->samplespending = 0;
+ atomic_store_relaxed(&E->bitsneeded, MINENTROPYBITS);
+ atomic_store_relaxed(&E->samplesneeded, MINSAMPLES);
E->consolidate = false;
mutex_exit(&E->lock);
}
Index: src/sys/kern/subr_cprng.c
diff -u src/sys/kern/subr_cprng.c:1.43 src/sys/kern/subr_cprng.c:1.43.4.1
--- src/sys/kern/subr_cprng.c:1.43 Fri May 13 09:40:25 2022
+++ src/sys/kern/subr_cprng.c Fri Aug 11 14:35:25 2023
@@ -1,4 +1,4 @@
-/* $NetBSD: subr_cprng.c,v 1.43 2022/05/13 09:40:25 riastradh Exp $ */
+/* $NetBSD: subr_cprng.c,v 1.43.4.1 2023/08/11 14:35:25 martin Exp $ */
/*-
* Copyright (c) 2019 The NetBSD Foundation, Inc.
@@ -52,7 +52,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: subr_cprng.c,v 1.43 2022/05/13 09:40:25 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: subr_cprng.c,v 1.43.4.1 2023/08/11 14:35:25 martin Exp $");
#include <sys/param.h>
#include <sys/types.h>
@@ -265,10 +265,39 @@ cprng_fini_cpu(void *ptr, void *cookie,
kmem_free(cc->cc_drbg, sizeof(*cc->cc_drbg));
}
+static void
+cprng_strong_reseed(struct cprng_strong *cprng, unsigned epoch,
+ struct cprng_cpu **ccp, int *sp)
+{
+ uint8_t seed[NIST_HASH_DRBG_SEEDLEN_BYTES];
+
+ /*
+ * Drop everything to extract a fresh seed from the entropy
+ * pool. entropy_extract may sleep on an adaptive lock, which
+ * invalidates our percpu(9) reference.
+ *
+ * This may race with reseeding in another thread, which is no
+ * big deal -- worst case, we rewind the entropy epoch here and
+ * cause the next caller to reseed again, and in the end we
+ * just reseed a couple more times than necessary.
+ */
+ splx(*sp);
+ percpu_putref(cprng->cs_percpu);
+ entropy_extract(seed, sizeof seed, 0);
+ *ccp = percpu_getref(cprng->cs_percpu);
+ *sp = splraiseipl(cprng->cs_iplcookie);
+
+ (*ccp)->cc_evcnt->reseed.ev_count++;
+ if (__predict_false(nist_hash_drbg_reseed((*ccp)->cc_drbg,
+ seed, sizeof seed, NULL, 0)))
+ panic("nist_hash_drbg_reseed");
+ explicit_memset(seed, 0, sizeof seed);
+ (*ccp)->cc_epoch = epoch;
+}
+
size_t
cprng_strong(struct cprng_strong *cprng, void *buf, size_t len, int flags)
{
- uint8_t seed[NIST_HASH_DRBG_SEEDLEN_BYTES];
struct cprng_cpu *cc;
unsigned epoch;
int s;
@@ -293,25 +322,13 @@ cprng_strong(struct cprng_strong *cprng,
/* If the entropy epoch has changed, (re)seed. */
epoch = entropy_epoch();
- if (__predict_false(epoch != cc->cc_epoch)) {
- entropy_extract(seed, sizeof seed, 0);
- cc->cc_evcnt->reseed.ev_count++;
- if (__predict_false(nist_hash_drbg_reseed(cc->cc_drbg,
- seed, sizeof seed, NULL, 0)))
- panic("nist_hash_drbg_reseed");
- explicit_memset(seed, 0, sizeof seed);
- cc->cc_epoch = epoch;
- }
+ if (__predict_false(epoch != cc->cc_epoch))
+ cprng_strong_reseed(cprng, epoch, &cc, &s);
/* Generate data. Failure here means it's time to reseed. */
if (__predict_false(nist_hash_drbg_generate(cc->cc_drbg, buf, len,
NULL, 0))) {
- entropy_extract(seed, sizeof seed, 0);
- cc->cc_evcnt->reseed.ev_count++;
- if (__predict_false(nist_hash_drbg_reseed(cc->cc_drbg,
- seed, sizeof seed, NULL, 0)))
- panic("nist_hash_drbg_reseed");
- explicit_memset(seed, 0, sizeof seed);
+ cprng_strong_reseed(cprng, epoch, &cc, &s);
if (__predict_false(nist_hash_drbg_generate(cc->cc_drbg,
buf, len, NULL, 0)))
panic("nist_hash_drbg_generate");
Index: src/sys/kern/subr_prf.c
diff -u src/sys/kern/subr_prf.c:1.196.2.1 src/sys/kern/subr_prf.c:1.196.2.2
--- src/sys/kern/subr_prf.c:1.196.2.1 Wed Feb 8 18:24:52 2023
+++ src/sys/kern/subr_prf.c Fri Aug 11 14:35:25 2023
@@ -1,4 +1,4 @@
-/* $NetBSD: subr_prf.c,v 1.196.2.1 2023/02/08 18:24:52 martin Exp $ */
+/* $NetBSD: subr_prf.c,v 1.196.2.2 2023/08/11 14:35:25 martin Exp $ */
/*-
* Copyright (c) 1986, 1988, 1991, 1993
@@ -37,7 +37,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: subr_prf.c,v 1.196.2.1 2023/02/08 18:24:52 martin Exp $");
+__KERNEL_RCSID(0, "$NetBSD: subr_prf.c,v 1.196.2.2 2023/08/11 14:35:25 martin Exp $");
#ifdef _KERNEL_OPT
#include "opt_ddb.h"
@@ -520,7 +520,7 @@ putchar(int c, int flags, struct tty *tp
#ifdef RND_PRINTF
if (__predict_true(kprintf_inited)) {
unsigned char ch = c;
- rnd_add_data(&rnd_printf_source, &ch, 1, 0);
+ rnd_add_data_intr(&rnd_printf_source, &ch, 1, 0);
}
#endif
}
@@ -1622,7 +1622,7 @@ done:
#ifdef RND_PRINTF
if (__predict_true(kprintf_inited))
- rnd_add_data(&rnd_printf_source, NULL, 0, 0);
+ rnd_add_data_intr(&rnd_printf_source, NULL, 0, 0);
#endif
return ret;
}
Index: src/sys/sys/rndio.h
diff -u src/sys/sys/rndio.h:1.2 src/sys/sys/rndio.h:1.2.50.1
--- src/sys/sys/rndio.h:1.2 Sun Sep 6 06:01:02 2015
+++ src/sys/sys/rndio.h Fri Aug 11 14:35:25 2023
@@ -1,4 +1,4 @@
-/* $NetBSD: rndio.h,v 1.2 2015/09/06 06:01:02 dholland Exp $ */
+/* $NetBSD: rndio.h,v 1.2.50.1 2023/08/11 14:35:25 martin Exp $ */
/*-
* Copyright (c) 1997 The NetBSD Foundation, Inc.
@@ -50,8 +50,7 @@ typedef struct {
} rndsave_t;
/* Statistics exposed by RNDGETPOOLSTAT */
-typedef struct
-{
+typedef struct {
uint32_t poolsize;
uint32_t threshold;
uint32_t maxentropy;
Index: src/sys/sys/rndsource.h
diff -u src/sys/sys/rndsource.h:1.7 src/sys/sys/rndsource.h:1.7.20.1
--- src/sys/sys/rndsource.h:1.7 Thu Apr 30 03:28:19 2020
+++ src/sys/sys/rndsource.h Fri Aug 11 14:35:25 2023
@@ -1,4 +1,4 @@
-/* $NetBSD: rndsource.h,v 1.7 2020/04/30 03:28:19 riastradh Exp $ */
+/* $NetBSD: rndsource.h,v 1.7.20.1 2023/08/11 14:35:25 martin Exp $ */
/*-
* Copyright (c) 2019 The NetBSD Foundation, Inc.
@@ -46,7 +46,7 @@ struct percpu;
/*
* struct rnd_delta_estimator
*
- * Unused. Preserved for ABI compatibility.
+ * State for the time-delta entropy estimation model.
*/
typedef struct rnd_delta_estimator {
uint64_t x;
@@ -67,14 +67,14 @@ typedef struct rnd_delta_estimator {
*/
struct krndsource {
LIST_ENTRY(krndsource) list; /* the linked list */
- char name[16]; /* device name */
- rnd_delta_t time_delta; /* unused */
- rnd_delta_t value_delta; /* unused */
- uint32_t total; /* number of bits added while cold */
- uint32_t type; /* type, RND_TYPE_* */
- uint32_t flags; /* flags, RND_FLAG_* */
- void *state; /* percpu (struct rndsource_cpu *) */
- size_t test_cnt; /* unused */
+ char name[16]; /* device name */
+ rnd_delta_t time_delta; /* time samples added while cold */
+ rnd_delta_t value_delta; /* value samples added whiel cold */
+ uint32_t total; /* number of bits added while cold */
+ uint32_t type; /* type, RND_TYPE_* */
+ uint32_t flags; /* flags, RND_FLAG_* */
+ void *state; /* percpu (struct rndsource_cpu *) */
+ size_t test_cnt; /* unused */
void (*get)(size_t, void *); /* pool wants N bytes (badly) */
void *getarg; /* argument to get-function */
void (*enable)(struct krndsource *, bool); /* unused */
@@ -94,6 +94,8 @@ void _rnd_add_uint64(struct krndsource *
void rnd_add_uint32(struct krndsource *, uint32_t);
void rnd_add_data(struct krndsource *, const void *, uint32_t, uint32_t);
+void rnd_add_data_intr(struct krndsource *, const void *, uint32_t,
+ uint32_t);
void rnd_add_data_sync(struct krndsource *, const void *, uint32_t,
uint32_t);
Index: src/tests/lib/libc/sys/t_getrandom.c
diff -u src/tests/lib/libc/sys/t_getrandom.c:1.4 src/tests/lib/libc/sys/t_getrandom.c:1.4.2.1
--- src/tests/lib/libc/sys/t_getrandom.c:1.4 Fri Mar 18 23:35:37 2022
+++ src/tests/lib/libc/sys/t_getrandom.c Fri Aug 11 14:35:25 2023
@@ -1,4 +1,4 @@
-/* $NetBSD: t_getrandom.c,v 1.4 2022/03/18 23:35:37 riastradh Exp $ */
+/* $NetBSD: t_getrandom.c,v 1.4.2.1 2023/08/11 14:35:25 martin Exp $ */
/*-
* Copyright (c) 2020 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
*/
#include <sys/cdefs.h>
-__RCSID("$NetBSD: t_getrandom.c,v 1.4 2022/03/18 23:35:37 riastradh Exp $");
+__RCSID("$NetBSD: t_getrandom.c,v 1.4.2.1 2023/08/11 14:35:25 martin Exp $");
#include <sys/param.h>
@@ -94,7 +94,7 @@ ATF_TC_BODY(getrandom_default, tc)
ATF_CHECK(n >= (ssize_t)MIN(256, sizeof buf));
ATF_CHECK((size_t)n <= sizeof buf);
ATF_CHECK(memcmp(buf, zero24, 24) != 0);
- ATF_CHECK(memcmp(buf + sizeof buf - 24, zero24, 24) != 0);
+ ATF_CHECK(memcmp(buf + n - 24, zero24, 24) != 0);
}
alarm(0);
}
@@ -117,7 +117,7 @@ ATF_TC_BODY(getrandom_nonblock, tc)
ATF_CHECK(n >= (ssize_t)MIN(256, sizeof buf));
ATF_CHECK((size_t)n <= sizeof buf);
ATF_CHECK(memcmp(buf, zero24, 24) != 0);
- ATF_CHECK(memcmp(buf + sizeof buf - 24, zero24, 24) != 0);
+ ATF_CHECK(memcmp(buf + n - 24, zero24, 24) != 0);
}
}
@@ -137,7 +137,7 @@ ATF_TC_BODY(getrandom_insecure, tc)
ATF_CHECK(n >= (ssize_t)MIN(256, sizeof buf));
ATF_CHECK((size_t)n <= sizeof buf);
ATF_CHECK(memcmp(buf, zero24, 24) != 0);
- ATF_CHECK(memcmp(buf + sizeof buf - 24, zero24, 24) != 0);
+ ATF_CHECK(memcmp(buf + n - 24, zero24, 24) != 0);
}
ATF_TC(getrandom_insecure_nonblock);
@@ -157,7 +157,7 @@ ATF_TC_BODY(getrandom_insecure_nonblock,
ATF_CHECK(n >= (ssize_t)MIN(256, sizeof buf));
ATF_CHECK((size_t)n <= sizeof buf);
ATF_CHECK(memcmp(buf, zero24, 24) != 0);
- ATF_CHECK(memcmp(buf + sizeof buf - 24, zero24, 24) != 0);
+ ATF_CHECK(memcmp(buf + n - 24, zero24, 24) != 0);
}
ATF_TC(getrandom_random);