diff --git a/Doc/library/os.rst b/Doc/library/os.rst index e6b6465eb99..785b080cff7 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -3733,14 +3733,21 @@ Miscellaneous Functions This function returns random bytes from an OS-specific randomness source. The returned data should be unpredictable enough for cryptographic applications, - though its exact quality depends on the OS implementation. On a Unix-like - system this will query ``/dev/urandom``, and on Windows it will use - ``CryptGenRandom()``. If a randomness source is not found, + though its exact quality depends on the OS implementation. + + On Linux, ``getrandom()`` syscall is used if available and the urandom + entropy pool is initialized (``getrandom()`` does not block). + On a Unix-like system this will query ``/dev/urandom``. On Windows, it + will use ``CryptGenRandom()``. If a randomness source is not found, :exc:`NotImplementedError` will be raised. For an easy-to-use interface to the random number generator provided by your platform, please see :class:`random.SystemRandom`. + .. versionchanged:: 3.5.2 + On Linux, if ``getrandom()`` blocks (the urandom entropy pool is not + initialized yet), fall back on reading ``/dev/urandom``. + .. versionchanged:: 3.5 On Linux 3.17 and newer, the ``getrandom()`` syscall is now used when available. On OpenBSD 5.6 and newer, the C ``getentropy()`` diff --git a/Misc/ACKS b/Misc/ACKS index 709ce942235..02e4821305d 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -200,6 +200,7 @@ Ian Bruntlett Floris Bruynooghe Matt Bryant Stan Bubrouski +Colm Buckley Erik de Bueger Jan-Hein Bührman Lars Buitinck diff --git a/Misc/NEWS b/Misc/NEWS index da2dc3ec8e3..918f8c82280 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -131,6 +131,10 @@ Core and Builtins Library ------- +- Issue #26839: On Linux, :func:`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. + - Issue #27164: In the zlib module, allow decompressing raw Deflate streams with a predefined zdict. Based on patch by Xiang Zhang. diff --git a/Python/random.c b/Python/random.c index 79157b8b6f2..ecfd44bbda3 100644 --- a/Python/random.c +++ b/Python/random.c @@ -6,6 +6,9 @@ # ifdef HAVE_SYS_STAT_H # include # endif +# ifdef HAVE_LINUX_RANDOM_H +# include +# endif # ifdef HAVE_GETRANDOM # include # elif defined(HAVE_GETRANDOM_SYSCALL) @@ -122,9 +125,13 @@ 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 */ static int getrandom_works = 1; - /* Use non-blocking /dev/urandom device. On Linux at boot, the getrandom() - * syscall blocks until /dev/urandom is initialized with enough entropy. */ - const int flags = 0; + + /* getrandom() on Linux will block if called before the kernel has + * initialized the urandom entropy pool. This will cause Python + * to hang on startup if called very early in the boot process - + * see https://bugs.python.org/issue26839. To avoid this, use the + * GRND_NONBLOCK flag. */ + const int flags = GRND_NONBLOCK; int n; if (!getrandom_works) @@ -168,6 +175,17 @@ py_getrandom(void *buffer, Py_ssize_t size, int raise) 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_works = 0; + return 0; + } if (errno == EINTR) { if (PyErr_CheckSignals()) { diff --git a/configure b/configure index 3216ad77572..c892a99f9d3 100755 --- a/configure +++ b/configure @@ -2876,6 +2876,7 @@ fi ac_config_headers="$ac_config_headers pyconfig.h" + ac_aux_dir= for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do 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 \ sys/stat.h sys/syscall.h sys/sys_domain.h sys/termio.h sys/time.h \ sys/times.h sys/types.h sys/uio.h sys/un.h sys/utsname.h sys/wait.h pty.h \ libutil.h sys/resource.h netpacket/packet.h sysexits.h bluetooth.h \ -bluetooth/bluetooth.h linux/tipc.h spawn.h util.h alloca.h endian.h \ +bluetooth/bluetooth.h linux/tipc.h linux/random.h spawn.h util.h alloca.h endian.h \ sys/endian.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` @@ -16325,12 +16326,13 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext #include #include + #include int main() { char buffer[1]; const size_t buflen = sizeof(buffer); - const int flags = 0; - /* ignore the result, Python checks for ENOSYS at runtime */ + const int flags = GRND_NONBLOCK; + /* ignore the result, Python checks for ENOSYS and EAGAIN at runtime */ (void)syscall(SYS_getrandom, buffer, buflen, flags); return 0; } diff --git a/configure.ac b/configure.ac index 4dc20d64f90..1c07c0569db 100644 --- a/configure.ac +++ b/configure.ac @@ -1881,7 +1881,7 @@ sys/param.h sys/select.h sys/sendfile.h sys/socket.h sys/statvfs.h \ sys/stat.h sys/syscall.h sys/sys_domain.h sys/termio.h sys/time.h \ sys/times.h sys/types.h sys/uio.h sys/un.h sys/utsname.h sys/wait.h pty.h \ libutil.h sys/resource.h netpacket/packet.h sysexits.h bluetooth.h \ -bluetooth/bluetooth.h linux/tipc.h spawn.h util.h alloca.h endian.h \ +bluetooth/bluetooth.h linux/tipc.h linux/random.h spawn.h util.h alloca.h endian.h \ sys/endian.h) AC_HEADER_DIRENT AC_HEADER_MAJOR @@ -5240,12 +5240,13 @@ AC_LINK_IFELSE( AC_LANG_SOURCE([[ #include #include + #include int main() { char buffer[1]; const size_t buflen = sizeof(buffer); - const int flags = 0; - /* ignore the result, Python checks for ENOSYS at runtime */ + const int flags = GRND_NONBLOCK; + /* ignore the result, Python checks for ENOSYS and EAGAIN at runtime */ (void)syscall(SYS_getrandom, buffer, buflen, flags); return 0; } diff --git a/pyconfig.h.in b/pyconfig.h.in index d432a821a57..78955355323 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -546,6 +546,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_TIPC_H +/* Define to 1 if you have the header file. */ +#undef HAVE_LINUX_RANDOM_H + /* Define to 1 if you have the `lockf' function. */ #undef HAVE_LOCKF