diff options
author | Victor Stinner <victor.stinner@gmail.com> | 2015-03-18 13:39:33 (GMT) |
---|---|---|
committer | Victor Stinner <victor.stinner@gmail.com> | 2015-03-18 13:39:33 (GMT) |
commit | 59f7fb29ec151ab64ace5056ab46cf8127a6dac2 (patch) | |
tree | 3374b848d6346e5ce6ba314d4dd294dad39f567d /Python/random.c | |
parent | 072248ec637a123dc348e6c1ffe075f85785111a (diff) | |
download | cpython-59f7fb29ec151ab64ace5056ab46cf8127a6dac2.zip cpython-59f7fb29ec151ab64ace5056ab46cf8127a6dac2.tar.gz cpython-59f7fb29ec151ab64ace5056ab46cf8127a6dac2.tar.bz2 |
Issue #22181: On Linux, os.urandom() now uses the new getrandom() syscall if
available, syscall introduced in the Linux kernel 3.17. It is more reliable
and more secure, because it avoids the need of a file descriptor and waits
until the kernel has enough entropy.
Diffstat (limited to 'Python/random.c')
-rw-r--r-- | Python/random.c | 90 |
1 files changed, 84 insertions, 6 deletions
diff --git a/Python/random.c b/Python/random.c index 39a389c..dcc3aab 100644 --- a/Python/random.c +++ b/Python/random.c @@ -1,11 +1,17 @@ #include "Python.h" #ifdef MS_WINDOWS -#include <windows.h> +# include <windows.h> #else -#include <fcntl.h> -#ifdef HAVE_SYS_STAT_H -#include <sys/stat.h> -#endif +# include <fcntl.h> +# ifdef HAVE_SYS_STAT_H +# include <sys/stat.h> +# endif +# ifdef HAVE_SYS_SYSCALL_H +# include <sys/syscall.h> +# if defined(__linux__) && defined(SYS_getrandom) +# define HAVE_GETRANDOM +# endif +# endif #endif #ifdef Py_DEBUG @@ -94,13 +100,65 @@ py_getentropy(unsigned char *buffer, Py_ssize_t size, int fatal) return 0; } -#else +#else /* !HAVE_GETENTROPY */ + +#ifdef HAVE_GETRANDOM +static int +py_getrandom(void *buffer, Py_ssize_t size, int raise) +{ + /* is getrandom() supported by the running kernel? + * need Linux kernel 3.17 or later */ + static int getrandom_works = 1; + /* Use /dev/urandom, block if the kernel has no entropy */ + const int flags = 0; + int n; + + if (!getrandom_works) + return 0; + + while (0 < size) { + errno = 0; + /* the libc doesn't expose getrandom() yet, see: + * https://sourceware.org/bugzilla/show_bug.cgi?id=17252 */ + n = syscall(SYS_getrandom, buffer, size, flags); + if (n < 0) { + if (errno == ENOSYS) { + getrandom_works = 0; + return 0; + } + + if (errno == EINTR) { + /* Note: EINTR should not occur with flags=0 */ + if (PyErr_CheckSignals()) { + if (!raise) + Py_FatalError("getrandom() interrupted by a signal"); + return -1; + } + /* retry getrandom() */ + continue; + } + + if (raise) + PyErr_SetFromErrno(PyExc_OSError); + else + Py_FatalError("getrandom() failed"); + return -1; + } + + buffer += n; + size -= n; + } + return 1; +} +#endif + static struct { int fd; dev_t st_dev; ino_t st_ino; } urandom_cache = { -1 }; + /* Read size bytes from /dev/urandom into buffer. Call Py_FatalError() on error. */ static void @@ -115,6 +173,13 @@ dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size) if (fd < 0) Py_FatalError("Failed to open /dev/urandom"); +#ifdef HAVE_GETRANDOM + if (py_getrandom(buffer, size, 0) == 1) + return; + /* getrandom() is not supported by the running kernel, fall back + * on reading /dev/urandom */ +#endif + while (0 < size) { do { @@ -140,10 +205,23 @@ dev_urandom_python(char *buffer, Py_ssize_t size) int fd; Py_ssize_t n; struct _Py_stat_struct st; +#ifdef HAVE_GETRANDOM + int res; +#endif if (size <= 0) return 0; +#ifdef HAVE_GETRANDOM + res = py_getrandom(buffer, size, 1); + if (res < 0) + return -1; + if (res == 1) + return 0; + /* getrandom() is not supported by the running kernel, fall back + * on reading /dev/urandom */ +#endif + if (urandom_cache.fd >= 0) { /* Does the fd point to the same thing as before? (issue #21207) */ if (_Py_fstat(urandom_cache.fd, &st) |