Skip to content

Commit a49a3aa

Browse files
committed
random-util.c: sync dev_urandom implementation to systemd-udev
Current dev_urandom() assumes that reading /dev/urandom will never block regardless if the random pool is fully initialized or not. This assumption is no longer applicable since linux kerrnel enforces the /dev/urandom entropy initialization from v5.18-rc2 with the commit: 48bff1053c17 ("random: opportunistically initialize on /dev/urandom reads"). With this, when we use the linux v5.18-rc2 or later, dev_urandom() will block if enough random pool is not supplied. It causes the boot delay, typically 1024msec(4msec * 256 = 1024msec) delay to fill the 256 bits entropy for the case CONFIG_HZ=250. To prevent this boot delay, this commit syncs dev_urandom() implementation to the systemd-udev. The systemd-udev implementation of reading /dev/urandom is as follows. - Try to get random with calling getrandom(GRND_INSECURE) - If kernel does not support GRND_INSECURE, fallback to GRND_NONBLOCK - If enough entropy is not supplied, fallback to reading /dev/urandom, this will block when the kernel version is v5.18-rc2 or later With this modification, dev_urandom() tries not to block as much as possible. This modification still keeps the backword compatibility, dev_random() will never block if the commit(48bff1053c17) is not applied to the linux kernel, the behavior is same as before in this case. Signed-off-by: Masahisa Kojima <[email protected]>
1 parent e98a667 commit a49a3aa

File tree

2 files changed

+35
-39
lines changed

2 files changed

+35
-39
lines changed

src/shared/missing.h

+4
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ static inline int getrandom(void *buffer, size_t count, unsigned flags) {
7979
#define GRND_RANDOM 0x0002
8080
#endif
8181

82+
#ifndef GRND_INSECURE
83+
#define GRND_INSECURE 0x0004
84+
#endif
85+
8286
#ifndef BTRFS_IOCTL_MAGIC
8387
#define BTRFS_IOCTL_MAGIC 0x94
8488
#endif

src/shared/random-util.c

+31-39
Original file line numberDiff line numberDiff line change
@@ -31,45 +31,37 @@
3131
#include "util.h"
3232

3333
int dev_urandom(void *p, size_t n) {
34-
static int have_syscall = -1;
35-
36-
_cleanup_close_ int fd = -1;
37-
int r;
38-
39-
/* Gathers some randomness from the kernel. This call will
40-
* never block, and will always return some data from the
41-
* kernel, regardless if the random pool is fully initialized
42-
* or not. It thus makes no guarantee for the quality of the
43-
* returned entropy, but is good enough for or usual usecases
44-
* of seeding the hash functions for hashtable */
45-
46-
/* Use the getrandom() syscall unless we know we don't have
47-
* it, or when the requested size is too large for it. */
48-
if (have_syscall != 0 || (size_t) (int) n != n) {
49-
r = getrandom(p, n, GRND_NONBLOCK);
50-
if (r == (int) n) {
51-
have_syscall = true;
52-
return 0;
53-
}
54-
55-
if (r < 0) {
56-
if (errno == ENOSYS)
57-
/* we lack the syscall, continue with
58-
* reading from /dev/urandom */
59-
have_syscall = false;
60-
else if (errno == EAGAIN)
61-
/* not enough entropy for now. Let's
62-
* remember to use the syscall the
63-
* next time, again, but also read
64-
* from /dev/urandom for now, which
65-
* doesn't care about the current
66-
* amount of entropy. */
67-
have_syscall = true;
68-
else
69-
return -errno;
70-
} else
71-
/* too short read? */
72-
return -ENODATA;
34+
static bool have_getrandom = true, have_grndinsecure = true;
35+
_cleanup_close_ int fd = -EBADF;
36+
37+
if (n == 0)
38+
return 0;
39+
40+
for (;;) {
41+
ssize_t l;
42+
43+
if (!have_getrandom)
44+
break;
45+
46+
l = getrandom(p, n, have_grndinsecure ? GRND_INSECURE : GRND_NONBLOCK);
47+
if (l > 0) {
48+
if ((size_t) l == n)
49+
return 0; /* Done reading, success. */
50+
p = (uint8_t *) p + l;
51+
n -= l;
52+
continue; /* Interrupted by a signal; keep going. */
53+
} else if (l == 0)
54+
break; /* Weird, so fallback to /dev/urandom. */
55+
else if (errno == ENOSYS) {
56+
have_getrandom = false;
57+
break; /* No syscall, so fallback to /dev/urandom. */
58+
} else if (errno == EINVAL && have_grndinsecure) {
59+
have_grndinsecure = false;
60+
continue; /* No GRND_INSECURE; fallback to GRND_NONBLOCK. */
61+
} else if (errno == EAGAIN && !have_grndinsecure)
62+
break; /* Will block, but no GRND_INSECURE, so fallback to /dev/urandom. */
63+
64+
break; /* Unexpected, so just give up and fallback to /dev/urandom. */
7365
}
7466

7567
fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);

0 commit comments

Comments
 (0)