@@ -77,57 +77,23 @@ win32_urandom(unsigned char *buffer, Py_ssize_t size, int raise)
7777 return 0 ;
7878}
7979
80- /* Issue #25003: Don't use getentropy() on Solaris (available since
81- * Solaris 11.3), it is blocking whereas os.urandom() should not block. */
82- #elif defined(HAVE_GETENTROPY ) && !defined(sun )
83- #define PY_GETENTROPY 1
84-
85- /* Fill buffer with size pseudo-random bytes generated by getentropy().
86- Return 0 on success, or raise an exception and return -1 on error.
87-
88- If raise is zero, don't raise an exception on error. */
89- static int
90- py_getentropy (char * buffer , Py_ssize_t size , int raise )
91- {
92- while (size > 0 ) {
93- Py_ssize_t len = Py_MIN (size , 256 );
94- int res ;
95-
96- if (raise ) {
97- Py_BEGIN_ALLOW_THREADS
98- res = getentropy (buffer , len );
99- Py_END_ALLOW_THREADS
100- }
101- else {
102- res = getentropy (buffer , len );
103- }
104-
105- if (res < 0 ) {
106- if (raise ) {
107- PyErr_SetFromErrno (PyExc_OSError );
108- }
109- return -1 ;
110- }
111-
112- buffer += len ;
113- size -= len ;
114- }
115- return 0 ;
116- }
117-
118- #else
80+ #else /* !MS_WINDOWS */
11981
12082#if defined(HAVE_GETRANDOM ) || defined(HAVE_GETRANDOM_SYSCALL )
12183#define PY_GETRANDOM 1
12284
123- /* Call getrandom()
85+ /* Call getrandom() to get random bytes:
86+
12487 - Return 1 on success
125- - Return 0 if getrandom() syscall is not available (failed with ENOSYS or
126- EPERM) or if getrandom(GRND_NONBLOCK) failed with EAGAIN (system urandom
127- not initialized yet) and raise=0.
88+ - Return 0 if getrandom() is not available (failed with ENOSYS or EPERM),
89+ or if getrandom(GRND_NONBLOCK) failed with EAGAIN (system urandom not
90+ initialized yet) and raise=0.
12891 - Raise an exception (if raise is non-zero) and return -1 on error:
129- getrandom() failed with EINTR and the Python signal handler raised an
130- exception, or getrandom() failed with a different error. */
92+ if getrandom() failed with EINTR, raise is non-zero and the Python signal
93+ handler raised an exception, or if getrandom() failed with a different
94+ error.
95+
96+ getrandom() is retried if it failed with EINTR: interrupted by a signal. */
13197static int
13298py_getrandom (void * buffer , Py_ssize_t size , int blocking , int raise )
13399{
@@ -148,7 +114,8 @@ py_getrandom(void *buffer, Py_ssize_t size, int blocking, int raise)
148114 while (0 < size ) {
149115#ifdef sun
150116 /* Issue #26735: On Solaris, getrandom() is limited to returning up
151- to 1024 bytes */
117+ to 1024 bytes. Call it multiple times if more bytes are
118+ requested. */
152119 n = Py_MIN (size , 1024 );
153120#else
154121 n = Py_MIN (size , LONG_MAX );
@@ -179,18 +146,19 @@ py_getrandom(void *buffer, Py_ssize_t size, int blocking, int raise)
179146#endif
180147
181148 if (n < 0 ) {
182- /* ENOSYS: getrandom() syscall not supported by the kernel (but
183- * maybe supported by the host which built Python). EPERM:
184- * getrandom() syscall blocked by SECCOMP or something else. */
149+ /* ENOSYS: the syscall is not supported by the kernel.
150+ EPERM: the syscall is blocked by a security policy (ex: SECCOMP)
151+ or something else. */
185152 if (errno == ENOSYS || errno == EPERM ) {
186153 getrandom_works = 0 ;
187154 return 0 ;
188155 }
189156
190157 /* getrandom(GRND_NONBLOCK) fails with EAGAIN if the system urandom
191- is not initialiazed yet. For _PyRandom_Init(), we ignore their
158+ is not initialiazed yet. For _PyRandom_Init(), we ignore the
192159 error and fall back on reading /dev/urandom which never blocks,
193- even if the system urandom is not initialized yet. */
160+ even if the system urandom is not initialized yet:
161+ see the PEP 524. */
194162 if (errno == EAGAIN && !raise && !blocking ) {
195163 return 0 ;
196164 }
@@ -217,43 +185,119 @@ py_getrandom(void *buffer, Py_ssize_t size, int blocking, int raise)
217185 }
218186 return 1 ;
219187}
220- #endif
188+
189+ #elif defined(HAVE_GETENTROPY )
190+ #define PY_GETENTROPY 1
191+
192+ /* Fill buffer with size pseudo-random bytes generated by getentropy():
193+
194+ - Return 1 on success
195+ - Return 0 if getentropy() syscall is not available (failed with ENOSYS or
196+ EPERM).
197+ - Raise an exception (if raise is non-zero) and return -1 on error:
198+ if getentropy() failed with EINTR, raise is non-zero and the Python signal
199+ handler raised an exception, or if getentropy() failed with a different
200+ error.
201+
202+ getentropy() is retried if it failed with EINTR: interrupted by a signal. */
203+ static int
204+ py_getentropy (char * buffer , Py_ssize_t size , int raise )
205+ {
206+ /* Is getentropy() supported by the running kernel? Set to 0 if
207+ getentropy() failed with ENOSYS or EPERM. */
208+ static int getentropy_works = 1 ;
209+
210+ if (!getentropy_works ) {
211+ return 0 ;
212+ }
213+
214+ while (size > 0 ) {
215+ /* getentropy() is limited to returning up to 256 bytes. Call it
216+ multiple times if more bytes are requested. */
217+ Py_ssize_t len = Py_MIN (size , 256 );
218+ int res ;
219+
220+ if (raise ) {
221+ Py_BEGIN_ALLOW_THREADS
222+ res = getentropy (buffer , len );
223+ Py_END_ALLOW_THREADS
224+ }
225+ else {
226+ res = getentropy (buffer , len );
227+ }
228+
229+ if (res < 0 ) {
230+ /* ENOSYS: the syscall is not supported by the running kernel.
231+ EPERM: the syscall is blocked by a security policy (ex: SECCOMP)
232+ or something else. */
233+ if (errno == ENOSYS || errno == EPERM ) {
234+ getentropy_works = 0 ;
235+ return 0 ;
236+ }
237+
238+ if (errno == EINTR ) {
239+ if (raise ) {
240+ if (PyErr_CheckSignals ()) {
241+ return -1 ;
242+ }
243+ }
244+
245+ /* retry getentropy() if it was interrupted by a signal */
246+ continue ;
247+ }
248+
249+ if (raise ) {
250+ PyErr_SetFromErrno (PyExc_OSError );
251+ }
252+ return -1 ;
253+ }
254+
255+ buffer += len ;
256+ size -= len ;
257+ }
258+ return 1 ;
259+ }
260+ #endif /* defined(HAVE_GETENTROPY) && !defined(sun) */
261+
221262
222263static struct {
223264 int fd ;
224265 dev_t st_dev ;
225266 ino_t st_ino ;
226267} urandom_cache = { -1 };
227268
269+ /* Read random bytes from the /dev/urandom device:
270+
271+ - Return 0 on success
272+ - Raise an exception (if raise is non-zero) and return -1 on error
273+
274+ Possible causes of errors:
275+
276+ - open() failed with ENOENT, ENXIO, ENODEV, EACCES: the /dev/urandom device
277+ was not found. For example, it was removed manually or not exposed in a
278+ chroot or container.
279+ - open() failed with a different error
280+ - fstat() failed
281+ - read() failed or returned 0
228282
229- /* Read 'size' random bytes from py_getrandom(). Fall back on reading from
230- /dev/urandom if getrandom() is not available.
283+ read() is retried if it failed with EINTR: interrupted by a signal.
231284
232- Return 0 on success. Raise an exception (if raise is non-zero) and return -1
233- on error. */
285+ The file descriptor of the device is kept open between calls to avoid using
286+ many file descriptors when run in parallel from multiple threads:
287+ see the issue #18756.
288+
289+ st_dev and st_ino fields of the file descriptor (from fstat()) are cached to
290+ check if the file descriptor was replaced by a different file (which is
291+ likely a bug in the application): see the issue #21207.
292+
293+ If the file descriptor was closed or replaced, open a new file descriptor
294+ but don't close the old file descriptor: it probably points to something
295+ important for some third-party code. */
234296static int
235- dev_urandom (char * buffer , Py_ssize_t size , int blocking , int raise )
297+ dev_urandom (char * buffer , Py_ssize_t size , int raise )
236298{
237299 int fd ;
238300 Py_ssize_t n ;
239- #ifdef PY_GETRANDOM
240- int res ;
241- #endif
242-
243- assert (size > 0 );
244-
245- #ifdef PY_GETRANDOM
246- res = py_getrandom (buffer , size , blocking , raise );
247- if (res < 0 ) {
248- return -1 ;
249- }
250- if (res == 1 ) {
251- return 0 ;
252- }
253- /* getrandom() failed with ENOSYS or EPERM,
254- fall back on reading /dev/urandom */
255- #endif
256-
257301
258302 if (raise ) {
259303 struct _Py_stat_struct st ;
@@ -275,9 +319,10 @@ dev_urandom(char *buffer, Py_ssize_t size, int blocking, int raise)
275319 fd = _Py_open ("/dev/urandom" , O_RDONLY );
276320 if (fd < 0 ) {
277321 if (errno == ENOENT || errno == ENXIO ||
278- errno == ENODEV || errno == EACCES )
322+ errno == ENODEV || errno == EACCES ) {
279323 PyErr_SetString (PyExc_NotImplementedError ,
280324 "/dev/urandom (or equivalent) not found" );
325+ }
281326 /* otherwise, keep the OSError exception raised by _Py_open() */
282327 return -1 ;
283328 }
@@ -349,8 +394,8 @@ dev_urandom_close(void)
349394 urandom_cache .fd = -1 ;
350395 }
351396}
397+ #endif /* !MS_WINDOWS */
352398
353- #endif
354399
355400/* Fill buffer with pseudo-random bytes generated by a linear congruent
356401 generator (LCG):
@@ -373,14 +418,56 @@ lcg_urandom(unsigned int x0, unsigned char *buffer, size_t size)
373418 }
374419}
375420
376- /* If raise is zero:
377- - Don't raise exceptions on error
378- - Don't call PyErr_CheckSignals() on EINTR (retry directly the interrupted
379- syscall)
380- - Don't release the GIL to call syscalls. */
421+ /* Read random bytes:
422+
423+ - Return 0 on success
424+ - Raise an exception (if raise is non-zero) and return -1 on error
425+
426+ Used sources of entropy ordered by preference, preferred source first:
427+
428+ - CryptGenRandom() on Windows
429+ - getrandom() function (ex: Linux and Solaris): call py_getrandom()
430+ - getentropy() function (ex: OpenBSD): call py_getentropy()
431+ - /dev/urandom device
432+
433+ Read from the /dev/urandom device if getrandom() or getentropy() function
434+ is not available or does not work.
435+
436+ Prefer getrandom() over getentropy() because getrandom() supports blocking
437+ and non-blocking mode: see the PEP 524. Python requires non-blocking RNG at
438+ startup to initialize its hash secret, but os.urandom() must block until the
439+ system urandom is initialized (at least on Linux 3.17 and newer).
440+
441+ Prefer getrandom() and getentropy() over reading directly /dev/urandom
442+ because these functions don't need file descriptors and so avoid ENFILE or
443+ EMFILE errors (too many open files): see the issue #18756.
444+
445+ Only the getrandom() function supports non-blocking mode.
446+
447+ Only use RNG running in the kernel. They are more secure because it is
448+ harder to get the internal state of a RNG running in the kernel land than a
449+ RNG running in the user land. The kernel has a direct access to the hardware
450+ and has access to hardware RNG, they are used as entropy sources.
451+
452+ Note: the OpenSSL RAND_pseudo_bytes() function does not automatically reseed
453+ its RNG on fork(), two child processes (with the same pid) generate the same
454+ random numbers: see issue #18747. Kernel RNGs don't have this issue,
455+ they have access to good quality entropy sources.
456+
457+ If raise is zero:
458+
459+ - Don't raise an exception on error
460+ - Don't call the Python signal handler (don't call PyErr_CheckSignals()) if
461+ a function fails with EINTR: retry directly the interrupted function
462+ - Don't release the GIL to call functions.
463+ */
381464static int
382465pyurandom (void * buffer , Py_ssize_t size , int blocking , int raise )
383466{
467+ #if defined(PY_GETRANDOM ) || defined(PY_GETENTROPY )
468+ int res ;
469+ #endif
470+
384471 if (size < 0 ) {
385472 if (raise ) {
386473 PyErr_Format (PyExc_ValueError ,
@@ -395,10 +482,25 @@ pyurandom(void *buffer, Py_ssize_t size, int blocking, int raise)
395482
396483#ifdef MS_WINDOWS
397484 return win32_urandom ((unsigned char * )buffer , size , raise );
398- #elif defined(PY_GETENTROPY )
399- return py_getentropy (buffer , size , raise );
400485#else
401- return dev_urandom (buffer , size , blocking , raise );
486+
487+ #if defined(PY_GETRANDOM ) || defined(PY_GETENTROPY )
488+ #ifdef PY_GETRANDOM
489+ res = py_getrandom (buffer , size , blocking , raise );
490+ #else
491+ res = py_getentropy (buffer , size , raise );
492+ #endif
493+ if (res < 0 ) {
494+ return -1 ;
495+ }
496+ if (res == 1 ) {
497+ return 0 ;
498+ }
499+ /* getrandom() or getentropy() function is not available: failed with
500+ ENOSYS or EPERM. Fall back on reading from /dev/urandom. */
501+ #endif
502+
503+ return dev_urandom (buffer , size , raise );
402504#endif
403505}
404506
@@ -491,8 +593,6 @@ _PyRandom_Fini(void)
491593 CryptReleaseContext (hCryptProv , 0 );
492594 hCryptProv = 0 ;
493595 }
494- #elif defined(PY_GETENTROPY )
495- /* nothing to clean */
496596#else
497597 dev_urandom_close ();
498598#endif
0 commit comments