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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
|
From 079c591fefa308803535e6529689a8583bbc2efa Mon Sep 17 00:00:00 2001
From: Victor Stinner <victor.stinner@gmail.com>
Date: Tue, 20 Sep 2016 22:26:18 +0200
Subject: [PATCH] Cleanup random.c
Issue #27955: modify py_getrnadom() and dev_urandom()
* Add comments from Python 3.7
* PEP 7 style: add {...}
---
Python/random.c | 81 +++++++++++++++++++++++++++++++++++++--------------------
1 file changed, 53 insertions(+), 28 deletions(-)
diff --git a/Python/random.c b/Python/random.c
index 3119872..5582e39 100644
--- a/Python/random.c
+++ b/Python/random.c
@@ -119,11 +119,20 @@ py_getentropy(unsigned char *buffer, Py_ssize_t size, int fatal)
#if defined(HAVE_GETRANDOM) || defined(HAVE_GETRANDOM_SYSCALL)
#define PY_GETRANDOM 1
+/* Call getrandom()
+ - Return 1 on success
+ - Return 0 if getrandom() syscall is not available (failed with ENOSYS)
+ or if getrandom(GRND_NONBLOCK) failed with EAGAIN (system urandom
+ not initialized yet) and raise=0.
+ - Raise an exception (if raise is non-zero) and return -1 on error:
+ getrandom() failed with EINTR and the Python signal handler raised an
+ exception, or getrandom() failed with a different error. */
static int
py_getrandom(void *buffer, Py_ssize_t size, int raise)
{
- /* Is getrandom() supported by the running kernel?
- * Need Linux kernel 3.17 or newer, or Solaris 11.3 or newer */
+ /* Is getrandom() supported by the running kernel? Set to 0 if getrandom()
+ failed with ENOSYS. Need Linux kernel 3.17 or newer, or Solaris
+ 11.3 or newer */
static int getrandom_works = 1;
/* getrandom() on Linux will block if called before the kernel has
@@ -134,8 +143,9 @@ py_getrandom(void *buffer, Py_ssize_t size, int raise)
const int flags = GRND_NONBLOCK;
long n;
- if (!getrandom_works)
+ if (!getrandom_works) {
return 0;
+ }
while (0 < size) {
#ifdef sun
@@ -171,36 +181,42 @@ py_getrandom(void *buffer, Py_ssize_t size, int raise)
#endif
if (n < 0) {
+ /* ENOSYS: getrandom() syscall not supported by the kernel (but
+ * maybe supported by the host which built Python). */
if (errno == ENOSYS) {
getrandom_works = 0;
return 0;
}
if (errno == EAGAIN) {
- /* If we failed with EAGAIN, the entropy pool was
- * uninitialized. In this case, we return failure to fall
- * back to reading from /dev/urandom.
- *
- * Note: In this case the data read will not be random so
- * should not be used for cryptographic purposes. Retaining
- * the existing semantics for practical purposes. */
+ /* getrandom(GRND_NONBLOCK) fails with EAGAIN if the system
+ urandom is not initialiazed yet. In this case, fall back on
+ reading from /dev/urandom.
+
+ Note: In this case the data read will not be random so
+ should not be used for cryptographic purposes. Retaining
+ the existing semantics for practical purposes. */
getrandom_works = 0;
return 0;
}
if (errno == EINTR) {
if (PyErr_CheckSignals()) {
- if (!raise)
+ if (!raise) {
Py_FatalError("getrandom() interrupted by a signal");
+ }
return -1;
}
+
/* retry getrandom() */
continue;
}
- if (raise)
+ if (raise) {
PyErr_SetFromErrno(PyExc_OSError);
- else
+ }
+ else {
Py_FatalError("getrandom() failed");
+ }
return -1;
}
@@ -218,7 +234,9 @@ static struct {
} urandom_cache = { -1 };
-/* Read size bytes from /dev/urandom into buffer.
+/* Read 'size' random bytes from py_getrandom(). Fall back on reading from
+ /dev/urandom if getrandom() is not available.
+
Call Py_FatalError() on error. */
static void
dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size)
@@ -229,24 +247,26 @@ dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size)
assert (0 < size);
#ifdef PY_GETRANDOM
- if (py_getrandom(buffer, size, 0) == 1)
+ if (py_getrandom(buffer, size, 0) == 1) {
return;
- /* getrandom() is not supported by the running kernel, fall back
- * on reading /dev/urandom */
+ }
+ /* getrandom() failed with ENOSYS,
+ fall back on reading /dev/urandom */
#endif
fd = _Py_open_noraise("/dev/urandom", O_RDONLY);
- if (fd < 0)
+ if (fd < 0) {
Py_FatalError("Failed to open /dev/urandom");
+ }
while (0 < size)
{
do {
n = read(fd, buffer, (size_t)size);
} while (n < 0 && errno == EINTR);
- if (n <= 0)
- {
- /* stop on error or if read(size) returned 0 */
+
+ if (n <= 0) {
+ /* read() failed or returned 0 bytes */
Py_FatalError("Failed to read bytes from /dev/urandom");
break;
}
@@ -256,8 +276,10 @@ dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size)
close(fd);
}
-/* Read size bytes from /dev/urandom into buffer.
- Return 0 on success, raise an exception and return -1 on error. */
+/* Read 'size' random bytes from py_getrandom(). Fall back on reading from
+ /dev/urandom if getrandom() is not available.
+
+ Return 0 on success. Raise an exception and return -1 on error. */
static int
dev_urandom_python(char *buffer, Py_ssize_t size)
{
@@ -273,12 +295,14 @@ dev_urandom_python(char *buffer, Py_ssize_t size)
#ifdef PY_GETRANDOM
res = py_getrandom(buffer, size, 1);
- if (res < 0)
+ if (res < 0) {
return -1;
- if (res == 1)
+ }
+ if (res == 1) {
return 0;
- /* getrandom() is not supported by the running kernel, fall back
- * on reading /dev/urandom */
+ }
+ /* getrandom() failed with ENOSYS,
+ fall back on reading /dev/urandom */
#endif
if (urandom_cache.fd >= 0) {
@@ -325,8 +349,9 @@ dev_urandom_python(char *buffer, Py_ssize_t size)
do {
n = _Py_read(fd, buffer, (size_t)size);
- if (n == -1)
+ if (n == -1) {
return -1;
+ }
if (n == 0) {
PyErr_Format(PyExc_RuntimeError,
"Failed to read %zi bytes from /dev/urandom",
From 2b9aca0055805921cd8783bb3ac2cccfa20e83dc Mon Sep 17 00:00:00 2001
From: Victor Stinner <victor.stinner@gmail.com>
Date: Tue, 20 Sep 2016 22:46:02 +0200
Subject: [PATCH] Catch EPERM error in py_getrandom()
Issue #27955: Fallback on reading /dev/urandom device when the getrandom()
syscall fails with EPERM, for example when blocked by SECCOMP.
---
Misc/NEWS | 3 +++
Python/random.c | 15 ++++++++-------
2 files changed, 11 insertions(+), 7 deletions(-)
diff --git a/Python/random.c b/Python/random.c
index 5582e39..f2ada5f 100644
--- a/Python/random.c
+++ b/Python/random.c
@@ -121,8 +121,8 @@ py_getentropy(unsigned char *buffer, Py_ssize_t size, int fatal)
/* Call getrandom()
- Return 1 on success
- - Return 0 if getrandom() syscall is not available (failed with ENOSYS)
- or if getrandom(GRND_NONBLOCK) failed with EAGAIN (system urandom
+ - Return 0 if getrandom() syscall is not available (failed with ENOSYS or
+ EPERM) or if getrandom(GRND_NONBLOCK) failed with EAGAIN (system urandom
not initialized yet) and raise=0.
- Raise an exception (if raise is non-zero) and return -1 on error:
getrandom() failed with EINTR and the Python signal handler raised an
@@ -131,7 +131,7 @@ static int
py_getrandom(void *buffer, Py_ssize_t size, int raise)
{
/* Is getrandom() supported by the running kernel? Set to 0 if getrandom()
- failed with ENOSYS. Need Linux kernel 3.17 or newer, or Solaris
+ failed with ENOSYS or EPERM. Need Linux kernel 3.17 or newer, or Solaris
11.3 or newer */
static int getrandom_works = 1;
@@ -182,8 +182,9 @@ py_getrandom(void *buffer, Py_ssize_t size, int raise)
if (n < 0) {
/* ENOSYS: getrandom() syscall not supported by the kernel (but
- * maybe supported by the host which built Python). */
- if (errno == ENOSYS) {
+ * maybe supported by the host which built Python). EPERM:
+ * getrandom() syscall blocked by SECCOMP or something else. */
+ if (errno == ENOSYS || errno == EPERM) {
getrandom_works = 0;
return 0;
}
@@ -250,7 +251,7 @@ dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size)
if (py_getrandom(buffer, size, 0) == 1) {
return;
}
- /* getrandom() failed with ENOSYS,
+ /* getrandom() failed with ENOSYS or EPERM,
fall back on reading /dev/urandom */
#endif
@@ -301,7 +302,7 @@ dev_urandom_python(char *buffer, Py_ssize_t size)
if (res == 1) {
return 0;
}
- /* getrandom() failed with ENOSYS,
+ /* getrandom() failed with ENOSYS or EPERM,
fall back on reading /dev/urandom */
#endif
|