1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
|
From 95c871b7b912f39539777ac222ef7f8798bb0225 Mon Sep 17 00:00:00 2001
From: Masahisa Kojima <kojima.masahisa@socionext.com>
Date: Thu, 25 Apr 2024 17:23:10 +0900
Subject: [PATCH] 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.
Upstream-Status: Backport [a49a3aaa460add6ae7ea208b4cac630e56fe1180]
Signed-off-by: Masahisa Kojima <kojima.masahisa@socionext.com>
---
src/shared/missing.h | 4 +++
src/shared/random-util.c | 70 ++++++++++++++++++----------------------
2 files changed, 35 insertions(+), 39 deletions(-)
diff --git a/src/shared/missing.h b/src/shared/missing.h
index 1967840cdbf3..1caec0f9207c 100644
--- a/src/shared/missing.h
+++ b/src/shared/missing.h
@@ -79,6 +79,10 @@ static inline int getrandom(void *buffer, size_t count, unsigned flags) {
#define GRND_RANDOM 0x0002
#endif
+#ifndef GRND_INSECURE
+#define GRND_INSECURE 0x0004
+#endif
+
#ifndef BTRFS_IOCTL_MAGIC
#define BTRFS_IOCTL_MAGIC 0x94
#endif
diff --git a/src/shared/random-util.c b/src/shared/random-util.c
index 01a28c8ef4e9..852b00e4ce2b 100644
--- a/src/shared/random-util.c
+++ b/src/shared/random-util.c
@@ -31,45 +31,37 @@
#include "util.h"
int dev_urandom(void *p, size_t n) {
- static int have_syscall = -1;
-
- _cleanup_close_ int fd = -1;
- int r;
-
- /* Gathers some randomness from the kernel. This call will
- * never block, and will always return some data from the
- * kernel, regardless if the random pool is fully initialized
- * or not. It thus makes no guarantee for the quality of the
- * returned entropy, but is good enough for or usual usecases
- * of seeding the hash functions for hashtable */
-
- /* Use the getrandom() syscall unless we know we don't have
- * it, or when the requested size is too large for it. */
- if (have_syscall != 0 || (size_t) (int) n != n) {
- r = getrandom(p, n, GRND_NONBLOCK);
- if (r == (int) n) {
- have_syscall = true;
- return 0;
- }
-
- if (r < 0) {
- if (errno == ENOSYS)
- /* we lack the syscall, continue with
- * reading from /dev/urandom */
- have_syscall = false;
- else if (errno == EAGAIN)
- /* not enough entropy for now. Let's
- * remember to use the syscall the
- * next time, again, but also read
- * from /dev/urandom for now, which
- * doesn't care about the current
- * amount of entropy. */
- have_syscall = true;
- else
- return -errno;
- } else
- /* too short read? */
- return -ENODATA;
+ static bool have_getrandom = true, have_grndinsecure = true;
+ _cleanup_close_ int fd = -EBADF;
+
+ if (n == 0)
+ return 0;
+
+ for (;;) {
+ ssize_t l;
+
+ if (!have_getrandom)
+ break;
+
+ l = getrandom(p, n, have_grndinsecure ? GRND_INSECURE : GRND_NONBLOCK);
+ if (l > 0) {
+ if ((size_t) l == n)
+ return 0; /* Done reading, success. */
+ p = (uint8_t *) p + l;
+ n -= l;
+ continue; /* Interrupted by a signal; keep going. */
+ } else if (l == 0)
+ break; /* Weird, so fallback to /dev/urandom. */
+ else if (errno == ENOSYS) {
+ have_getrandom = false;
+ break; /* No syscall, so fallback to /dev/urandom. */
+ } else if (errno == EINVAL && have_grndinsecure) {
+ have_grndinsecure = false;
+ continue; /* No GRND_INSECURE; fallback to GRND_NONBLOCK. */
+ } else if (errno == EAGAIN && !have_grndinsecure)
+ break; /* Will block, but no GRND_INSECURE, so fallback to /dev/urandom. */
+
+ break; /* Unexpected, so just give up and fallback to /dev/urandom. */
}
fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
|