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.
This commit is contained in:
Victor Stinner 2016-06-07 11:21:42 +02:00
parent 6827fd867b
commit dddf4849ec
7 changed files with 48 additions and 12 deletions

View File

@ -3733,14 +3733,21 @@ Miscellaneous Functions
This function returns random bytes from an OS-specific randomness source. The This function returns random bytes from an OS-specific randomness source. The
returned data should be unpredictable enough for cryptographic applications, returned data should be unpredictable enough for cryptographic applications,
though its exact quality depends on the OS implementation. On a Unix-like though its exact quality depends on the OS implementation.
system this will query ``/dev/urandom``, and on Windows it will use
``CryptGenRandom()``. If a randomness source is not found, 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. :exc:`NotImplementedError` will be raised.
For an easy-to-use interface to the random number generator For an easy-to-use interface to the random number generator
provided by your platform, please see :class:`random.SystemRandom`. 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 .. versionchanged:: 3.5
On Linux 3.17 and newer, the ``getrandom()`` syscall is now used On Linux 3.17 and newer, the ``getrandom()`` syscall is now used
when available. On OpenBSD 5.6 and newer, the C ``getentropy()`` when available. On OpenBSD 5.6 and newer, the C ``getentropy()``

View File

@ -200,6 +200,7 @@ Ian Bruntlett
Floris Bruynooghe Floris Bruynooghe
Matt Bryant Matt Bryant
Stan Bubrouski Stan Bubrouski
Colm Buckley
Erik de Bueger Erik de Bueger
Jan-Hein Bührman Jan-Hein Bührman
Lars Buitinck Lars Buitinck

View File

@ -131,6 +131,10 @@ Core and Builtins
Library 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 - Issue #27164: In the zlib module, allow decompressing raw Deflate streams
with a predefined zdict. Based on patch by Xiang Zhang. with a predefined zdict. Based on patch by Xiang Zhang.

View File

@ -6,6 +6,9 @@
# ifdef HAVE_SYS_STAT_H # ifdef HAVE_SYS_STAT_H
# include <sys/stat.h> # include <sys/stat.h>
# endif # endif
# ifdef HAVE_LINUX_RANDOM_H
# include <linux/random.h>
# endif
# ifdef HAVE_GETRANDOM # ifdef HAVE_GETRANDOM
# include <sys/random.h> # include <sys/random.h>
# elif defined(HAVE_GETRANDOM_SYSCALL) # 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? /* Is getrandom() supported by the running kernel?
* Need Linux kernel 3.17 or newer, or Solaris 11.3 or newer */ * Need Linux kernel 3.17 or newer, or Solaris 11.3 or newer */
static int getrandom_works = 1; 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. */ /* getrandom() on Linux will block if called before the kernel has
const int flags = 0; * 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; int n;
if (!getrandom_works) if (!getrandom_works)
@ -168,6 +175,17 @@ py_getrandom(void *buffer, Py_ssize_t size, int raise)
getrandom_works = 0; getrandom_works = 0;
return 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 (errno == EINTR) {
if (PyErr_CheckSignals()) { if (PyErr_CheckSignals()) {

8
configure vendored
View File

@ -2876,6 +2876,7 @@ fi
ac_config_headers="$ac_config_headers pyconfig.h" ac_config_headers="$ac_config_headers pyconfig.h"
ac_aux_dir= ac_aux_dir=
for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
if test -f "$ac_dir/install-sh"; then 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/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 \ 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 \ 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 sys/endian.h
do : do :
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` 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 <unistd.h> #include <unistd.h>
#include <sys/syscall.h> #include <sys/syscall.h>
#include <linux/random.h>
int main() { int main() {
char buffer[1]; char buffer[1];
const size_t buflen = sizeof(buffer); const size_t buflen = sizeof(buffer);
const int flags = 0; const int flags = GRND_NONBLOCK;
/* ignore the result, Python checks for ENOSYS at runtime */ /* ignore the result, Python checks for ENOSYS and EAGAIN at runtime */
(void)syscall(SYS_getrandom, buffer, buflen, flags); (void)syscall(SYS_getrandom, buffer, buflen, flags);
return 0; return 0;
} }

View File

@ -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/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 \ 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 \ 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) sys/endian.h)
AC_HEADER_DIRENT AC_HEADER_DIRENT
AC_HEADER_MAJOR AC_HEADER_MAJOR
@ -5240,12 +5240,13 @@ AC_LINK_IFELSE(
AC_LANG_SOURCE([[ AC_LANG_SOURCE([[
#include <unistd.h> #include <unistd.h>
#include <sys/syscall.h> #include <sys/syscall.h>
#include <linux/random.h>
int main() { int main() {
char buffer[1]; char buffer[1];
const size_t buflen = sizeof(buffer); const size_t buflen = sizeof(buffer);
const int flags = 0; const int flags = GRND_NONBLOCK;
/* ignore the result, Python checks for ENOSYS at runtime */ /* ignore the result, Python checks for ENOSYS and EAGAIN at runtime */
(void)syscall(SYS_getrandom, buffer, buflen, flags); (void)syscall(SYS_getrandom, buffer, buflen, flags);
return 0; return 0;
} }

View File

@ -546,6 +546,9 @@
/* Define to 1 if you have the <linux/tipc.h> header file. */ /* Define to 1 if you have the <linux/tipc.h> header file. */
#undef HAVE_LINUX_TIPC_H #undef HAVE_LINUX_TIPC_H
/* Define to 1 if you have the <linux/random.h> header file. */
#undef HAVE_LINUX_RANDOM_H
/* Define to 1 if you have the `lockf' function. */ /* Define to 1 if you have the `lockf' function. */
#undef HAVE_LOCKF #undef HAVE_LOCKF