Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit dddf484

Browse files
committed
os.urandom() doesn't block on Linux anymore
Issue #26839: On Linux, os.urandom() now calls getrandom() with GRND_NONBLOCK to fall back on reading /dev/urandom if the urandom entropy pool is not initialized yet. Patch written by Colm Buckley.
1 parent 6827fd8 commit dddf484

7 files changed

Lines changed: 48 additions & 12 deletions

File tree

Doc/library/os.rst

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3733,14 +3733,21 @@ Miscellaneous Functions
37333733

37343734
This function returns random bytes from an OS-specific randomness source. The
37353735
returned data should be unpredictable enough for cryptographic applications,
3736-
though its exact quality depends on the OS implementation. On a Unix-like
3737-
system this will query ``/dev/urandom``, and on Windows it will use
3738-
``CryptGenRandom()``. If a randomness source is not found,
3736+
though its exact quality depends on the OS implementation.
3737+
3738+
On Linux, ``getrandom()`` syscall is used if available and the urandom
3739+
entropy pool is initialized (``getrandom()`` does not block).
3740+
On a Unix-like system this will query ``/dev/urandom``. On Windows, it
3741+
will use ``CryptGenRandom()``. If a randomness source is not found,
37393742
:exc:`NotImplementedError` will be raised.
37403743

37413744
For an easy-to-use interface to the random number generator
37423745
provided by your platform, please see :class:`random.SystemRandom`.
37433746

3747+
.. versionchanged:: 3.5.2
3748+
On Linux, if ``getrandom()`` blocks (the urandom entropy pool is not
3749+
initialized yet), fall back on reading ``/dev/urandom``.
3750+
37443751
.. versionchanged:: 3.5
37453752
On Linux 3.17 and newer, the ``getrandom()`` syscall is now used
37463753
when available. On OpenBSD 5.6 and newer, the C ``getentropy()``

Misc/ACKS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ Ian Bruntlett
200200
Floris Bruynooghe
201201
Matt Bryant
202202
Stan Bubrouski
203+
Colm Buckley
203204
Erik de Bueger
204205
Jan-Hein Bührman
205206
Lars Buitinck

Misc/NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,10 @@ Core and Builtins
131131
Library
132132
-------
133133

134+
- Issue #26839: On Linux, :func:`os.urandom` now calls ``getrandom()`` with
135+
``GRND_NONBLOCK`` to fall back on reading ``/dev/urandom`` if the urandom
136+
entropy pool is not initialized yet. Patch written by Colm Buckley.
137+
134138
- Issue #27164: In the zlib module, allow decompressing raw Deflate streams
135139
with a predefined zdict. Based on patch by Xiang Zhang.
136140

Python/random.c

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
# ifdef HAVE_SYS_STAT_H
77
# include <sys/stat.h>
88
# endif
9+
# ifdef HAVE_LINUX_RANDOM_H
10+
# include <linux/random.h>
11+
# endif
912
# ifdef HAVE_GETRANDOM
1013
# include <sys/random.h>
1114
# elif defined(HAVE_GETRANDOM_SYSCALL)
@@ -122,9 +125,13 @@ py_getrandom(void *buffer, Py_ssize_t size, int raise)
122125
/* Is getrandom() supported by the running kernel?
123126
* Need Linux kernel 3.17 or newer, or Solaris 11.3 or newer */
124127
static int getrandom_works = 1;
125-
/* Use non-blocking /dev/urandom device. On Linux at boot, the getrandom()
126-
* syscall blocks until /dev/urandom is initialized with enough entropy. */
127-
const int flags = 0;
128+
129+
/* getrandom() on Linux will block if called before the kernel has
130+
* initialized the urandom entropy pool. This will cause Python
131+
* to hang on startup if called very early in the boot process -
132+
* see https://bugs.python.org/issue26839. To avoid this, use the
133+
* GRND_NONBLOCK flag. */
134+
const int flags = GRND_NONBLOCK;
128135
int n;
129136

130137
if (!getrandom_works)
@@ -168,6 +175,17 @@ py_getrandom(void *buffer, Py_ssize_t size, int raise)
168175
getrandom_works = 0;
169176
return 0;
170177
}
178+
if (errno == EAGAIN) {
179+
/* If we failed with EAGAIN, the entropy pool was
180+
* uninitialized. In this case, we return failure to fall
181+
* back to reading from /dev/urandom.
182+
*
183+
* Note: In this case the data read will not be random so
184+
* should not be used for cryptographic purposes. Retaining
185+
* the existing semantics for practical purposes. */
186+
getrandom_works = 0;
187+
return 0;
188+
}
171189

172190
if (errno == EINTR) {
173191
if (PyErr_CheckSignals()) {

configure

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2876,6 +2876,7 @@ fi
28762876
ac_config_headers="$ac_config_headers pyconfig.h"
28772877

28782878

2879+
28792880
ac_aux_dir=
28802881
for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
28812882
if test -f "$ac_dir/install-sh"; then
@@ -7568,7 +7569,7 @@ sys/param.h sys/select.h sys/sendfile.h sys/socket.h sys/statvfs.h \
75687569
sys/stat.h sys/syscall.h sys/sys_domain.h sys/termio.h sys/time.h \
75697570
sys/times.h sys/types.h sys/uio.h sys/un.h sys/utsname.h sys/wait.h pty.h \
75707571
libutil.h sys/resource.h netpacket/packet.h sysexits.h bluetooth.h \
7571-
bluetooth/bluetooth.h linux/tipc.h spawn.h util.h alloca.h endian.h \
7572+
bluetooth/bluetooth.h linux/tipc.h linux/random.h spawn.h util.h alloca.h endian.h \
75727573
sys/endian.h
75737574
do :
75747575
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
@@ -16325,12 +16326,13 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
1632516326
1632616327
#include <unistd.h>
1632716328
#include <sys/syscall.h>
16329+
#include <linux/random.h>
1632816330
1632916331
int main() {
1633016332
char buffer[1];
1633116333
const size_t buflen = sizeof(buffer);
16332-
const int flags = 0;
16333-
/* ignore the result, Python checks for ENOSYS at runtime */
16334+
const int flags = GRND_NONBLOCK;
16335+
/* ignore the result, Python checks for ENOSYS and EAGAIN at runtime */
1633416336
(void)syscall(SYS_getrandom, buffer, buflen, flags);
1633516337
return 0;
1633616338
}

configure.ac

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1881,7 +1881,7 @@ sys/param.h sys/select.h sys/sendfile.h sys/socket.h sys/statvfs.h \
18811881
sys/stat.h sys/syscall.h sys/sys_domain.h sys/termio.h sys/time.h \
18821882
sys/times.h sys/types.h sys/uio.h sys/un.h sys/utsname.h sys/wait.h pty.h \
18831883
libutil.h sys/resource.h netpacket/packet.h sysexits.h bluetooth.h \
1884-
bluetooth/bluetooth.h linux/tipc.h spawn.h util.h alloca.h endian.h \
1884+
bluetooth/bluetooth.h linux/tipc.h linux/random.h spawn.h util.h alloca.h endian.h \
18851885
sys/endian.h)
18861886
AC_HEADER_DIRENT
18871887
AC_HEADER_MAJOR
@@ -5240,12 +5240,13 @@ AC_LINK_IFELSE(
52405240
AC_LANG_SOURCE([[
52415241
#include <unistd.h>
52425242
#include <sys/syscall.h>
5243+
#include <linux/random.h>
52435244
52445245
int main() {
52455246
char buffer[1];
52465247
const size_t buflen = sizeof(buffer);
5247-
const int flags = 0;
5248-
/* ignore the result, Python checks for ENOSYS at runtime */
5248+
const int flags = GRND_NONBLOCK;
5249+
/* ignore the result, Python checks for ENOSYS and EAGAIN at runtime */
52495250
(void)syscall(SYS_getrandom, buffer, buflen, flags);
52505251
return 0;
52515252
}

pyconfig.h.in

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,9 @@
546546
/* Define to 1 if you have the <linux/tipc.h> header file. */
547547
#undef HAVE_LINUX_TIPC_H
548548

549+
/* Define to 1 if you have the <linux/random.h> header file. */
550+
#undef HAVE_LINUX_RANDOM_H
551+
549552
/* Define to 1 if you have the `lockf' function. */
550553
#undef HAVE_LOCKF
551554

0 commit comments

Comments
 (0)