diff options
author | Richard Purdie <richard.purdie@linuxfoundation.org> | 2017-01-22 11:17:26 +0000 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2017-01-23 12:05:21 +0000 |
commit | a0336965aa2e2215a274ff7d2ebae8c40443b1c5 (patch) | |
tree | 3972ef50c9ced030e13fd09a1656e4902f278482 /meta/recipes-devtools/python/python3 | |
parent | 6cea270c544ceb14cbd44f4e5f4e51c3a3dcb143 (diff) | |
download | poky-a0336965aa2e2215a274ff7d2ebae8c40443b1c5.tar.gz |
python3: Add upstream random.c fixes for recent glibc
python3 fails to work with recent glibc versions on older hosts, giving
errors like:
Fatal Python error: getentropy() failed
Aborted
This breaks buildtools-tarball and hence eSDK. This patch backports the
changes to random.c from upstream that address the problem.
(From OE-Core rev: 126b2c47b1806b53fbd9a4706bc48bc7c4efd3be)
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta/recipes-devtools/python/python3')
-rw-r--r-- | meta/recipes-devtools/python/python3/upstream-random-fixes.patch | 721 |
1 files changed, 721 insertions, 0 deletions
diff --git a/meta/recipes-devtools/python/python3/upstream-random-fixes.patch b/meta/recipes-devtools/python/python3/upstream-random-fixes.patch new file mode 100644 index 0000000000..0d9152ccd7 --- /dev/null +++ b/meta/recipes-devtools/python/python3/upstream-random-fixes.patch | |||
@@ -0,0 +1,721 @@ | |||
1 | This patch updates random.c to match upstream python's code at revision | ||
2 | 8125d9a8152b. This addresses various issues around problems with glibc 2.24 | ||
3 | and 2.25 such that python would fail to start with: | ||
4 | |||
5 | [rpurdie@centos7 ~]$ /tmp/t2/sysroots/x86_64-pokysdk-linux/usr/bin/python3 | ||
6 | Fatal Python error: getentropy() failed | ||
7 | Aborted | ||
8 | |||
9 | (taken from our buildtools-tarball also breaks eSDK) | ||
10 | |||
11 | Upstream-Status: Backport | ||
12 | |||
13 | # HG changeset patch | ||
14 | # User Victor Stinner <victor.stinner@gmail.com> | ||
15 | # Date 1483957133 -3600 | ||
16 | # Node ID 8125d9a8152b79e712cb09c7094b9129b9bcea86 | ||
17 | # Parent 337461574c90281630751b6095c4e1baf380cf7d | ||
18 | Issue #29157: Prefer getrandom() over getentropy() | ||
19 | |||
20 | Copy and then adapt Python/random.c from default branch. Difference between 3.5 | ||
21 | and default branches: | ||
22 | |||
23 | * Python 3.5 only uses getrandom() in non-blocking mode: flags=GRND_NONBLOCK | ||
24 | * If getrandom() fails with EAGAIN: py_getrandom() immediately fails and | ||
25 | remembers that getrandom() doesn't work. | ||
26 | * Python 3.5 has no _PyOS_URandomNonblock() function: _PyOS_URandom() | ||
27 | works in non-blocking mode on Python 3.5 | ||
28 | |||
29 | RP 2017/1/22 | ||
30 | |||
31 | Index: Python-3.5.2/Python/random.c | ||
32 | =================================================================== | ||
33 | --- Python-3.5.2.orig/Python/random.c | ||
34 | +++ Python-3.5.2/Python/random.c | ||
35 | @@ -1,6 +1,9 @@ | ||
36 | #include "Python.h" | ||
37 | #ifdef MS_WINDOWS | ||
38 | # include <windows.h> | ||
39 | +/* All sample MSDN wincrypt programs include the header below. It is at least | ||
40 | + * required with Min GW. */ | ||
41 | +# include <wincrypt.h> | ||
42 | #else | ||
43 | # include <fcntl.h> | ||
44 | # ifdef HAVE_SYS_STAT_H | ||
45 | @@ -36,10 +39,9 @@ win32_urandom_init(int raise) | ||
46 | return 0; | ||
47 | |||
48 | error: | ||
49 | - if (raise) | ||
50 | + if (raise) { | ||
51 | PyErr_SetFromWindowsErr(0); | ||
52 | - else | ||
53 | - Py_FatalError("Failed to initialize Windows random API (CryptoGen)"); | ||
54 | + } | ||
55 | return -1; | ||
56 | } | ||
57 | |||
58 | @@ -52,8 +54,9 @@ win32_urandom(unsigned char *buffer, Py_ | ||
59 | |||
60 | if (hCryptProv == 0) | ||
61 | { | ||
62 | - if (win32_urandom_init(raise) == -1) | ||
63 | + if (win32_urandom_init(raise) == -1) { | ||
64 | return -1; | ||
65 | + } | ||
66 | } | ||
67 | |||
68 | while (size > 0) | ||
69 | @@ -62,11 +65,9 @@ win32_urandom(unsigned char *buffer, Py_ | ||
70 | if (!CryptGenRandom(hCryptProv, (DWORD)chunk, buffer)) | ||
71 | { | ||
72 | /* CryptGenRandom() failed */ | ||
73 | - if (raise) | ||
74 | + if (raise) { | ||
75 | PyErr_SetFromWindowsErr(0); | ||
76 | - else | ||
77 | - Py_FatalError("Failed to initialized the randomized hash " | ||
78 | - "secret using CryptoGen)"); | ||
79 | + } | ||
80 | return -1; | ||
81 | } | ||
82 | buffer += chunk; | ||
83 | @@ -75,55 +76,29 @@ win32_urandom(unsigned char *buffer, Py_ | ||
84 | return 0; | ||
85 | } | ||
86 | |||
87 | -/* Issue #25003: Don't use getentropy() on Solaris (available since | ||
88 | - * Solaris 11.3), it is blocking whereas os.urandom() should not block. */ | ||
89 | -#elif defined(HAVE_GETENTROPY) && !defined(sun) | ||
90 | -#define PY_GETENTROPY 1 | ||
91 | - | ||
92 | -/* Fill buffer with size pseudo-random bytes generated by getentropy(). | ||
93 | - Return 0 on success, or raise an exception and return -1 on error. | ||
94 | - | ||
95 | - If fatal is nonzero, call Py_FatalError() instead of raising an exception | ||
96 | - on error. */ | ||
97 | -static int | ||
98 | -py_getentropy(unsigned char *buffer, Py_ssize_t size, int fatal) | ||
99 | -{ | ||
100 | - while (size > 0) { | ||
101 | - Py_ssize_t len = Py_MIN(size, 256); | ||
102 | - int res; | ||
103 | - | ||
104 | - if (!fatal) { | ||
105 | - Py_BEGIN_ALLOW_THREADS | ||
106 | - res = getentropy(buffer, len); | ||
107 | - Py_END_ALLOW_THREADS | ||
108 | - | ||
109 | - if (res < 0) { | ||
110 | - PyErr_SetFromErrno(PyExc_OSError); | ||
111 | - return -1; | ||
112 | - } | ||
113 | - } | ||
114 | - else { | ||
115 | - res = getentropy(buffer, len); | ||
116 | - if (res < 0) | ||
117 | - Py_FatalError("getentropy() failed"); | ||
118 | - } | ||
119 | - | ||
120 | - buffer += len; | ||
121 | - size -= len; | ||
122 | - } | ||
123 | - return 0; | ||
124 | -} | ||
125 | - | ||
126 | -#else | ||
127 | +#else /* !MS_WINDOWS */ | ||
128 | |||
129 | #if defined(HAVE_GETRANDOM) || defined(HAVE_GETRANDOM_SYSCALL) | ||
130 | #define PY_GETRANDOM 1 | ||
131 | |||
132 | +/* Call getrandom() to get random bytes: | ||
133 | + | ||
134 | + - Return 1 on success | ||
135 | + - Return 0 if getrandom() is not available (failed with ENOSYS or EPERM), | ||
136 | + or if getrandom(GRND_NONBLOCK) failed with EAGAIN (system urandom not | ||
137 | + initialized yet). | ||
138 | + - Raise an exception (if raise is non-zero) and return -1 on error: | ||
139 | + if getrandom() failed with EINTR, raise is non-zero and the Python signal | ||
140 | + handler raised an exception, or if getrandom() failed with a different | ||
141 | + error. | ||
142 | + | ||
143 | + getrandom() is retried if it failed with EINTR: interrupted by a signal. */ | ||
144 | static int | ||
145 | py_getrandom(void *buffer, Py_ssize_t size, int raise) | ||
146 | { | ||
147 | - /* Is getrandom() supported by the running kernel? | ||
148 | - * Need Linux kernel 3.17 or newer, or Solaris 11.3 or newer */ | ||
149 | + /* Is getrandom() supported by the running kernel? Set to 0 if getrandom() | ||
150 | + failed with ENOSYS or EPERM. Need Linux kernel 3.17 or newer, or Solaris | ||
151 | + 11.3 or newer */ | ||
152 | static int getrandom_works = 1; | ||
153 | |||
154 | /* getrandom() on Linux will block if called before the kernel has | ||
155 | @@ -132,84 +107,165 @@ py_getrandom(void *buffer, Py_ssize_t si | ||
156 | * see https://bugs.python.org/issue26839. To avoid this, use the | ||
157 | * GRND_NONBLOCK flag. */ | ||
158 | const int flags = GRND_NONBLOCK; | ||
159 | - int n; | ||
160 | + char *dest; | ||
161 | + long n; | ||
162 | |||
163 | - if (!getrandom_works) | ||
164 | + if (!getrandom_works) { | ||
165 | return 0; | ||
166 | + } | ||
167 | |||
168 | + dest = buffer; | ||
169 | while (0 < size) { | ||
170 | #ifdef sun | ||
171 | /* Issue #26735: On Solaris, getrandom() is limited to returning up | ||
172 | - to 1024 bytes */ | ||
173 | + to 1024 bytes. Call it multiple times if more bytes are | ||
174 | + requested. */ | ||
175 | n = Py_MIN(size, 1024); | ||
176 | #else | ||
177 | - n = size; | ||
178 | + n = Py_MIN(size, LONG_MAX); | ||
179 | #endif | ||
180 | |||
181 | errno = 0; | ||
182 | #ifdef HAVE_GETRANDOM | ||
183 | if (raise) { | ||
184 | Py_BEGIN_ALLOW_THREADS | ||
185 | - n = getrandom(buffer, n, flags); | ||
186 | + n = getrandom(dest, n, flags); | ||
187 | Py_END_ALLOW_THREADS | ||
188 | } | ||
189 | else { | ||
190 | - n = getrandom(buffer, n, flags); | ||
191 | + n = getrandom(dest, n, flags); | ||
192 | } | ||
193 | #else | ||
194 | /* On Linux, use the syscall() function because the GNU libc doesn't | ||
195 | - * expose the Linux getrandom() syscall yet. See: | ||
196 | - * https://sourceware.org/bugzilla/show_bug.cgi?id=17252 */ | ||
197 | + expose the Linux getrandom() syscall yet. See: | ||
198 | + https://sourceware.org/bugzilla/show_bug.cgi?id=17252 */ | ||
199 | if (raise) { | ||
200 | Py_BEGIN_ALLOW_THREADS | ||
201 | - n = syscall(SYS_getrandom, buffer, n, flags); | ||
202 | + n = syscall(SYS_getrandom, dest, n, flags); | ||
203 | Py_END_ALLOW_THREADS | ||
204 | } | ||
205 | else { | ||
206 | - n = syscall(SYS_getrandom, buffer, n, flags); | ||
207 | + n = syscall(SYS_getrandom, dest, n, flags); | ||
208 | } | ||
209 | #endif | ||
210 | |||
211 | if (n < 0) { | ||
212 | - if (errno == ENOSYS) { | ||
213 | + /* ENOSYS: the syscall is not supported by the kernel. | ||
214 | + EPERM: the syscall is blocked by a security policy (ex: SECCOMP) | ||
215 | + or something else. */ | ||
216 | + if (errno == ENOSYS || errno == EPERM) { | ||
217 | getrandom_works = 0; | ||
218 | return 0; | ||
219 | } | ||
220 | + | ||
221 | if (errno == EAGAIN) { | ||
222 | - /* If we failed with EAGAIN, the entropy pool was | ||
223 | - * uninitialized. In this case, we return failure to fall | ||
224 | - * back to reading from /dev/urandom. | ||
225 | - * | ||
226 | - * Note: In this case the data read will not be random so | ||
227 | - * should not be used for cryptographic purposes. Retaining | ||
228 | - * the existing semantics for practical purposes. */ | ||
229 | + /* getrandom(GRND_NONBLOCK) fails with EAGAIN if the system | ||
230 | + urandom is not initialiazed yet. In this case, fall back on | ||
231 | + reading from /dev/urandom. | ||
232 | + | ||
233 | + Note: In this case the data read will not be random so | ||
234 | + should not be used for cryptographic purposes. Retaining | ||
235 | + the existing semantics for practical purposes. */ | ||
236 | getrandom_works = 0; | ||
237 | return 0; | ||
238 | } | ||
239 | |||
240 | if (errno == EINTR) { | ||
241 | - if (PyErr_CheckSignals()) { | ||
242 | - if (!raise) | ||
243 | - Py_FatalError("getrandom() interrupted by a signal"); | ||
244 | - return -1; | ||
245 | + if (raise) { | ||
246 | + if (PyErr_CheckSignals()) { | ||
247 | + return -1; | ||
248 | + } | ||
249 | } | ||
250 | - /* retry getrandom() */ | ||
251 | + | ||
252 | + /* retry getrandom() if it was interrupted by a signal */ | ||
253 | continue; | ||
254 | } | ||
255 | |||
256 | - if (raise) | ||
257 | + if (raise) { | ||
258 | PyErr_SetFromErrno(PyExc_OSError); | ||
259 | - else | ||
260 | - Py_FatalError("getrandom() failed"); | ||
261 | + } | ||
262 | return -1; | ||
263 | } | ||
264 | |||
265 | - buffer += n; | ||
266 | + dest += n; | ||
267 | size -= n; | ||
268 | } | ||
269 | return 1; | ||
270 | } | ||
271 | -#endif | ||
272 | + | ||
273 | +#elif defined(HAVE_GETENTROPY) | ||
274 | +#define PY_GETENTROPY 1 | ||
275 | + | ||
276 | +/* Fill buffer with size pseudo-random bytes generated by getentropy(): | ||
277 | + | ||
278 | + - Return 1 on success | ||
279 | + - Return 0 if getentropy() syscall is not available (failed with ENOSYS or | ||
280 | + EPERM). | ||
281 | + - Raise an exception (if raise is non-zero) and return -1 on error: | ||
282 | + if getentropy() failed with EINTR, raise is non-zero and the Python signal | ||
283 | + handler raised an exception, or if getentropy() failed with a different | ||
284 | + error. | ||
285 | + | ||
286 | + getentropy() is retried if it failed with EINTR: interrupted by a signal. */ | ||
287 | +static int | ||
288 | +py_getentropy(char *buffer, Py_ssize_t size, int raise) | ||
289 | +{ | ||
290 | + /* Is getentropy() supported by the running kernel? Set to 0 if | ||
291 | + getentropy() failed with ENOSYS or EPERM. */ | ||
292 | + static int getentropy_works = 1; | ||
293 | + | ||
294 | + if (!getentropy_works) { | ||
295 | + return 0; | ||
296 | + } | ||
297 | + | ||
298 | + while (size > 0) { | ||
299 | + /* getentropy() is limited to returning up to 256 bytes. Call it | ||
300 | + multiple times if more bytes are requested. */ | ||
301 | + Py_ssize_t len = Py_MIN(size, 256); | ||
302 | + int res; | ||
303 | + | ||
304 | + if (raise) { | ||
305 | + Py_BEGIN_ALLOW_THREADS | ||
306 | + res = getentropy(buffer, len); | ||
307 | + Py_END_ALLOW_THREADS | ||
308 | + } | ||
309 | + else { | ||
310 | + res = getentropy(buffer, len); | ||
311 | + } | ||
312 | + | ||
313 | + if (res < 0) { | ||
314 | + /* ENOSYS: the syscall is not supported by the running kernel. | ||
315 | + EPERM: the syscall is blocked by a security policy (ex: SECCOMP) | ||
316 | + or something else. */ | ||
317 | + if (errno == ENOSYS || errno == EPERM) { | ||
318 | + getentropy_works = 0; | ||
319 | + return 0; | ||
320 | + } | ||
321 | + | ||
322 | + if (errno == EINTR) { | ||
323 | + if (raise) { | ||
324 | + if (PyErr_CheckSignals()) { | ||
325 | + return -1; | ||
326 | + } | ||
327 | + } | ||
328 | + | ||
329 | + /* retry getentropy() if it was interrupted by a signal */ | ||
330 | + continue; | ||
331 | + } | ||
332 | + | ||
333 | + if (raise) { | ||
334 | + PyErr_SetFromErrno(PyExc_OSError); | ||
335 | + } | ||
336 | + return -1; | ||
337 | + } | ||
338 | + | ||
339 | + buffer += len; | ||
340 | + size -= len; | ||
341 | + } | ||
342 | + return 1; | ||
343 | +} | ||
344 | +#endif /* defined(HAVE_GETENTROPY) && !defined(sun) */ | ||
345 | + | ||
346 | |||
347 | static struct { | ||
348 | int fd; | ||
349 | @@ -217,127 +273,123 @@ static struct { | ||
350 | ino_t st_ino; | ||
351 | } urandom_cache = { -1 }; | ||
352 | |||
353 | +/* Read random bytes from the /dev/urandom device: | ||
354 | |||
355 | -/* Read size bytes from /dev/urandom into buffer. | ||
356 | - Call Py_FatalError() on error. */ | ||
357 | -static void | ||
358 | -dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size) | ||
359 | -{ | ||
360 | - int fd; | ||
361 | - Py_ssize_t n; | ||
362 | + - Return 0 on success | ||
363 | + - Raise an exception (if raise is non-zero) and return -1 on error | ||
364 | |||
365 | - assert (0 < size); | ||
366 | + Possible causes of errors: | ||
367 | |||
368 | -#ifdef PY_GETRANDOM | ||
369 | - if (py_getrandom(buffer, size, 0) == 1) | ||
370 | - return; | ||
371 | - /* getrandom() is not supported by the running kernel, fall back | ||
372 | - * on reading /dev/urandom */ | ||
373 | -#endif | ||
374 | + - open() failed with ENOENT, ENXIO, ENODEV, EACCES: the /dev/urandom device | ||
375 | + was not found. For example, it was removed manually or not exposed in a | ||
376 | + chroot or container. | ||
377 | + - open() failed with a different error | ||
378 | + - fstat() failed | ||
379 | + - read() failed or returned 0 | ||
380 | |||
381 | - fd = _Py_open_noraise("/dev/urandom", O_RDONLY); | ||
382 | - if (fd < 0) | ||
383 | - Py_FatalError("Failed to open /dev/urandom"); | ||
384 | + read() is retried if it failed with EINTR: interrupted by a signal. | ||
385 | |||
386 | - while (0 < size) | ||
387 | - { | ||
388 | - do { | ||
389 | - n = read(fd, buffer, (size_t)size); | ||
390 | - } while (n < 0 && errno == EINTR); | ||
391 | - if (n <= 0) | ||
392 | - { | ||
393 | - /* stop on error or if read(size) returned 0 */ | ||
394 | - Py_FatalError("Failed to read bytes from /dev/urandom"); | ||
395 | - break; | ||
396 | - } | ||
397 | - buffer += n; | ||
398 | - size -= (Py_ssize_t)n; | ||
399 | - } | ||
400 | - close(fd); | ||
401 | -} | ||
402 | + The file descriptor of the device is kept open between calls to avoid using | ||
403 | + many file descriptors when run in parallel from multiple threads: | ||
404 | + see the issue #18756. | ||
405 | + | ||
406 | + st_dev and st_ino fields of the file descriptor (from fstat()) are cached to | ||
407 | + check if the file descriptor was replaced by a different file (which is | ||
408 | + likely a bug in the application): see the issue #21207. | ||
409 | |||
410 | -/* Read size bytes from /dev/urandom into buffer. | ||
411 | - Return 0 on success, raise an exception and return -1 on error. */ | ||
412 | + If the file descriptor was closed or replaced, open a new file descriptor | ||
413 | + but don't close the old file descriptor: it probably points to something | ||
414 | + important for some third-party code. */ | ||
415 | static int | ||
416 | -dev_urandom_python(char *buffer, Py_ssize_t size) | ||
417 | +dev_urandom(char *buffer, Py_ssize_t size, int raise) | ||
418 | { | ||
419 | int fd; | ||
420 | Py_ssize_t n; | ||
421 | - struct _Py_stat_struct st; | ||
422 | -#ifdef PY_GETRANDOM | ||
423 | - int res; | ||
424 | -#endif | ||
425 | |||
426 | - if (size <= 0) | ||
427 | - return 0; | ||
428 | + if (raise) { | ||
429 | + struct _Py_stat_struct st; | ||
430 | |||
431 | -#ifdef PY_GETRANDOM | ||
432 | - res = py_getrandom(buffer, size, 1); | ||
433 | - if (res < 0) | ||
434 | - return -1; | ||
435 | - if (res == 1) | ||
436 | - return 0; | ||
437 | - /* getrandom() is not supported by the running kernel, fall back | ||
438 | - * on reading /dev/urandom */ | ||
439 | -#endif | ||
440 | - | ||
441 | - if (urandom_cache.fd >= 0) { | ||
442 | - /* Does the fd point to the same thing as before? (issue #21207) */ | ||
443 | - if (_Py_fstat_noraise(urandom_cache.fd, &st) | ||
444 | - || st.st_dev != urandom_cache.st_dev | ||
445 | - || st.st_ino != urandom_cache.st_ino) { | ||
446 | - /* Something changed: forget the cached fd (but don't close it, | ||
447 | - since it probably points to something important for some | ||
448 | - third-party code). */ | ||
449 | - urandom_cache.fd = -1; | ||
450 | - } | ||
451 | - } | ||
452 | - if (urandom_cache.fd >= 0) | ||
453 | - fd = urandom_cache.fd; | ||
454 | - else { | ||
455 | - fd = _Py_open("/dev/urandom", O_RDONLY); | ||
456 | - if (fd < 0) { | ||
457 | - if (errno == ENOENT || errno == ENXIO || | ||
458 | - errno == ENODEV || errno == EACCES) | ||
459 | - PyErr_SetString(PyExc_NotImplementedError, | ||
460 | - "/dev/urandom (or equivalent) not found"); | ||
461 | - /* otherwise, keep the OSError exception raised by _Py_open() */ | ||
462 | - return -1; | ||
463 | - } | ||
464 | if (urandom_cache.fd >= 0) { | ||
465 | - /* urandom_fd was initialized by another thread while we were | ||
466 | - not holding the GIL, keep it. */ | ||
467 | - close(fd); | ||
468 | - fd = urandom_cache.fd; | ||
469 | + /* Does the fd point to the same thing as before? (issue #21207) */ | ||
470 | + if (_Py_fstat_noraise(urandom_cache.fd, &st) | ||
471 | + || st.st_dev != urandom_cache.st_dev | ||
472 | + || st.st_ino != urandom_cache.st_ino) { | ||
473 | + /* Something changed: forget the cached fd (but don't close it, | ||
474 | + since it probably points to something important for some | ||
475 | + third-party code). */ | ||
476 | + urandom_cache.fd = -1; | ||
477 | + } | ||
478 | } | ||
479 | + if (urandom_cache.fd >= 0) | ||
480 | + fd = urandom_cache.fd; | ||
481 | else { | ||
482 | - if (_Py_fstat(fd, &st)) { | ||
483 | - close(fd); | ||
484 | + fd = _Py_open("/dev/urandom", O_RDONLY); | ||
485 | + if (fd < 0) { | ||
486 | + if (errno == ENOENT || errno == ENXIO || | ||
487 | + errno == ENODEV || errno == EACCES) { | ||
488 | + PyErr_SetString(PyExc_NotImplementedError, | ||
489 | + "/dev/urandom (or equivalent) not found"); | ||
490 | + } | ||
491 | + /* otherwise, keep the OSError exception raised by _Py_open() */ | ||
492 | return -1; | ||
493 | } | ||
494 | + if (urandom_cache.fd >= 0) { | ||
495 | + /* urandom_fd was initialized by another thread while we were | ||
496 | + not holding the GIL, keep it. */ | ||
497 | + close(fd); | ||
498 | + fd = urandom_cache.fd; | ||
499 | + } | ||
500 | else { | ||
501 | - urandom_cache.fd = fd; | ||
502 | - urandom_cache.st_dev = st.st_dev; | ||
503 | - urandom_cache.st_ino = st.st_ino; | ||
504 | + if (_Py_fstat(fd, &st)) { | ||
505 | + close(fd); | ||
506 | + return -1; | ||
507 | + } | ||
508 | + else { | ||
509 | + urandom_cache.fd = fd; | ||
510 | + urandom_cache.st_dev = st.st_dev; | ||
511 | + urandom_cache.st_ino = st.st_ino; | ||
512 | + } | ||
513 | } | ||
514 | } | ||
515 | - } | ||
516 | |||
517 | - do { | ||
518 | - n = _Py_read(fd, buffer, (size_t)size); | ||
519 | - if (n == -1) | ||
520 | - return -1; | ||
521 | - if (n == 0) { | ||
522 | - PyErr_Format(PyExc_RuntimeError, | ||
523 | - "Failed to read %zi bytes from /dev/urandom", | ||
524 | - size); | ||
525 | + do { | ||
526 | + n = _Py_read(fd, buffer, (size_t)size); | ||
527 | + if (n == -1) | ||
528 | + return -1; | ||
529 | + if (n == 0) { | ||
530 | + PyErr_Format(PyExc_RuntimeError, | ||
531 | + "Failed to read %zi bytes from /dev/urandom", | ||
532 | + size); | ||
533 | + return -1; | ||
534 | + } | ||
535 | + | ||
536 | + buffer += n; | ||
537 | + size -= n; | ||
538 | + } while (0 < size); | ||
539 | + } | ||
540 | + else { | ||
541 | + fd = _Py_open_noraise("/dev/urandom", O_RDONLY); | ||
542 | + if (fd < 0) { | ||
543 | return -1; | ||
544 | } | ||
545 | |||
546 | - buffer += n; | ||
547 | - size -= n; | ||
548 | - } while (0 < size); | ||
549 | + while (0 < size) | ||
550 | + { | ||
551 | + do { | ||
552 | + n = read(fd, buffer, (size_t)size); | ||
553 | + } while (n < 0 && errno == EINTR); | ||
554 | |||
555 | + if (n <= 0) { | ||
556 | + /* stop on error or if read(size) returned 0 */ | ||
557 | + close(fd); | ||
558 | + return -1; | ||
559 | + } | ||
560 | + | ||
561 | + buffer += n; | ||
562 | + size -= n; | ||
563 | + } | ||
564 | + close(fd); | ||
565 | + } | ||
566 | return 0; | ||
567 | } | ||
568 | |||
569 | @@ -349,8 +401,8 @@ dev_urandom_close(void) | ||
570 | urandom_cache.fd = -1; | ||
571 | } | ||
572 | } | ||
573 | +#endif /* !MS_WINDOWS */ | ||
574 | |||
575 | -#endif | ||
576 | |||
577 | /* Fill buffer with pseudo-random bytes generated by a linear congruent | ||
578 | generator (LCG): | ||
579 | @@ -373,29 +425,98 @@ lcg_urandom(unsigned int x0, unsigned ch | ||
580 | } | ||
581 | } | ||
582 | |||
583 | -/* Fill buffer with size pseudo-random bytes from the operating system random | ||
584 | - number generator (RNG). It is suitable for most cryptographic purposes | ||
585 | - except long living private keys for asymmetric encryption. | ||
586 | +/* Read random bytes: | ||
587 | |||
588 | - Return 0 on success, raise an exception and return -1 on error. */ | ||
589 | -int | ||
590 | -_PyOS_URandom(void *buffer, Py_ssize_t size) | ||
591 | + - Return 0 on success | ||
592 | + - Raise an exception (if raise is non-zero) and return -1 on error | ||
593 | + | ||
594 | + Used sources of entropy ordered by preference, preferred source first: | ||
595 | + | ||
596 | + - CryptGenRandom() on Windows | ||
597 | + - getrandom() function (ex: Linux and Solaris): call py_getrandom() | ||
598 | + - getentropy() function (ex: OpenBSD): call py_getentropy() | ||
599 | + - /dev/urandom device | ||
600 | + | ||
601 | + Read from the /dev/urandom device if getrandom() or getentropy() function | ||
602 | + is not available or does not work. | ||
603 | + | ||
604 | + Prefer getrandom() over getentropy() because getrandom() supports blocking | ||
605 | + and non-blocking mode and Python requires non-blocking RNG at startup to | ||
606 | + initialize its hash secret: see the PEP 524. | ||
607 | + | ||
608 | + Prefer getrandom() and getentropy() over reading directly /dev/urandom | ||
609 | + because these functions don't need file descriptors and so avoid ENFILE or | ||
610 | + EMFILE errors (too many open files): see the issue #18756. | ||
611 | + | ||
612 | + Only use RNG running in the kernel. They are more secure because it is | ||
613 | + harder to get the internal state of a RNG running in the kernel land than a | ||
614 | + RNG running in the user land. The kernel has a direct access to the hardware | ||
615 | + and has access to hardware RNG, they are used as entropy sources. | ||
616 | + | ||
617 | + Note: the OpenSSL RAND_pseudo_bytes() function does not automatically reseed | ||
618 | + its RNG on fork(), two child processes (with the same pid) generate the same | ||
619 | + random numbers: see issue #18747. Kernel RNGs don't have this issue, | ||
620 | + they have access to good quality entropy sources. | ||
621 | + | ||
622 | + If raise is zero: | ||
623 | + | ||
624 | + - Don't raise an exception on error | ||
625 | + - Don't call the Python signal handler (don't call PyErr_CheckSignals()) if | ||
626 | + a function fails with EINTR: retry directly the interrupted function | ||
627 | + - Don't release the GIL to call functions. | ||
628 | +*/ | ||
629 | +static int | ||
630 | +pyurandom(void *buffer, Py_ssize_t size, int raise) | ||
631 | { | ||
632 | +#if defined(PY_GETRANDOM) || defined(PY_GETENTROPY) | ||
633 | + int res; | ||
634 | +#endif | ||
635 | + | ||
636 | if (size < 0) { | ||
637 | - PyErr_Format(PyExc_ValueError, | ||
638 | - "negative argument not allowed"); | ||
639 | + if (raise) { | ||
640 | + PyErr_Format(PyExc_ValueError, | ||
641 | + "negative argument not allowed"); | ||
642 | + } | ||
643 | return -1; | ||
644 | } | ||
645 | - if (size == 0) | ||
646 | + | ||
647 | + if (size == 0) { | ||
648 | return 0; | ||
649 | + } | ||
650 | |||
651 | #ifdef MS_WINDOWS | ||
652 | - return win32_urandom((unsigned char *)buffer, size, 1); | ||
653 | -#elif defined(PY_GETENTROPY) | ||
654 | - return py_getentropy(buffer, size, 0); | ||
655 | + return win32_urandom((unsigned char *)buffer, size, raise); | ||
656 | +#else | ||
657 | + | ||
658 | +#if defined(PY_GETRANDOM) || defined(PY_GETENTROPY) | ||
659 | +#ifdef PY_GETRANDOM | ||
660 | + res = py_getrandom(buffer, size, raise); | ||
661 | #else | ||
662 | - return dev_urandom_python((char*)buffer, size); | ||
663 | + res = py_getentropy(buffer, size, raise); | ||
664 | #endif | ||
665 | + if (res < 0) { | ||
666 | + return -1; | ||
667 | + } | ||
668 | + if (res == 1) { | ||
669 | + return 0; | ||
670 | + } | ||
671 | + /* getrandom() or getentropy() function is not available: failed with | ||
672 | + ENOSYS, EPERM or EAGAIN. Fall back on reading from /dev/urandom. */ | ||
673 | +#endif | ||
674 | + | ||
675 | + return dev_urandom(buffer, size, raise); | ||
676 | +#endif | ||
677 | +} | ||
678 | + | ||
679 | +/* Fill buffer with size pseudo-random bytes from the operating system random | ||
680 | + number generator (RNG). It is suitable for most cryptographic purposes | ||
681 | + except long living private keys for asymmetric encryption. | ||
682 | + | ||
683 | + Return 0 on success. Raise an exception and return -1 on error. */ | ||
684 | +int | ||
685 | +_PyOS_URandom(void *buffer, Py_ssize_t size) | ||
686 | +{ | ||
687 | + return pyurandom(buffer, size, 1); | ||
688 | } | ||
689 | |||
690 | void | ||
691 | @@ -436,13 +557,14 @@ _PyRandom_Init(void) | ||
692 | } | ||
693 | } | ||
694 | else { | ||
695 | -#ifdef MS_WINDOWS | ||
696 | - (void)win32_urandom(secret, secret_size, 0); | ||
697 | -#elif defined(PY_GETENTROPY) | ||
698 | - (void)py_getentropy(secret, secret_size, 1); | ||
699 | -#else | ||
700 | - dev_urandom_noraise(secret, secret_size); | ||
701 | -#endif | ||
702 | + int res; | ||
703 | + | ||
704 | + /* _PyRandom_Init() is called very early in the Python initialization | ||
705 | + and so exceptions cannot be used (use raise=0). */ | ||
706 | + res = pyurandom(secret, secret_size, 0); | ||
707 | + if (res < 0) { | ||
708 | + Py_FatalError("failed to get random numbers to initialize Python"); | ||
709 | + } | ||
710 | } | ||
711 | } | ||
712 | |||
713 | @@ -454,8 +576,6 @@ _PyRandom_Fini(void) | ||
714 | CryptReleaseContext(hCryptProv, 0); | ||
715 | hCryptProv = 0; | ||
716 | } | ||
717 | -#elif defined(PY_GETENTROPY) | ||
718 | - /* nothing to clean */ | ||
719 | #else | ||
720 | dev_urandom_close(); | ||
721 | #endif | ||