From dc47a19e576dcb358547608c5347a6bf78ced562 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 2 May 2014 22:06:44 +0200 Subject: Issue #21393: random.c: on Windows, close the hCryptProv handle at exit --- Python/random.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'Python/random.c') diff --git a/Python/random.c b/Python/random.c index 2941ba16af..b04d205a46 100644 --- a/Python/random.c +++ b/Python/random.c @@ -15,8 +15,6 @@ static int _Py_HashSecret_Initialized = 0; #endif #ifdef MS_WINDOWS -/* This handle is never explicitly released. Instead, the operating - system will release it when the process terminates. */ static HCRYPTPROV hCryptProv = 0; static int @@ -298,7 +296,12 @@ _PyRandom_Init(void) void _PyRandom_Fini(void) { -#ifndef MS_WINDOWS +#ifdef MS_WINDOWS + if (hCryptProv) { + CloseHandle(hCryptProv); + hCryptProv = 0; + } +#else dev_urandom_close(); #endif } -- cgit v1.2.1 From 0bb52d78d45567397897b61a0032b8dd5eb059c3 Mon Sep 17 00:00:00 2001 From: Tim Golden Date: Tue, 6 May 2014 13:29:45 +0100 Subject: Issue21393 Use CryptReleaseContext to release Crypt handle on Windows --- Python/random.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Python/random.c') diff --git a/Python/random.c b/Python/random.c index b04d205a46..a052b65725 100644 --- a/Python/random.c +++ b/Python/random.c @@ -298,7 +298,7 @@ _PyRandom_Fini(void) { #ifdef MS_WINDOWS if (hCryptProv) { - CloseHandle(hCryptProv); + CryptReleaseContext(hCryptProv, 0); hCryptProv = 0; } #else -- cgit v1.2.1 From 915c27520fb658ee0aa2e4d95487af700ceb5192 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sun, 21 Dec 2014 01:16:38 +0100 Subject: Issue #22585: On OpenBSD 5.6 and newer, os.urandom() now calls getentropy(), instead of reading /dev/urandom, to get pseudo-random bytes. --- Python/random.c | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) (limited to 'Python/random.c') diff --git a/Python/random.c b/Python/random.c index 654612ba29..93d300dae4 100644 --- a/Python/random.c +++ b/Python/random.c @@ -36,7 +36,7 @@ error: } /* Fill buffer with size pseudo-random bytes generated by the Windows CryptoGen - API. Return 0 on success, or -1 on error. */ + API. Return 0 on success, or raise an exception and return -1 on error. */ static int win32_urandom(unsigned char *buffer, Py_ssize_t size, int raise) { @@ -66,10 +66,35 @@ win32_urandom(unsigned char *buffer, Py_ssize_t size, int raise) } return 0; } -#endif /* MS_WINDOWS */ +#elif HAVE_GETENTROPY +/* Fill buffer with size pseudo-random bytes generated by getentropy(). + Return 0 on success, or raise an exception and return -1 on error. -#ifndef MS_WINDOWS + If fatal is nonzero, call Py_FatalError() instead of raising an exception + on error. */ +static int +py_getentropy(unsigned char *buffer, Py_ssize_t size, int fatal) +{ + while (size > 0) { + Py_ssize_t len = Py_MIN(size, 256); + int res = getentropy(buffer, len); + if (res < 0) { + if (fatal) { + Py_FatalError("getentropy() failed"); + } + else { + PyErr_SetFromErrno(PyExc_OSError); + return -1; + } + } + buffer += len; + size -= len; + } + return 0; +} + +#else static struct { int fd; dev_t st_dev; @@ -201,7 +226,7 @@ dev_urandom_close(void) } } -#endif /* MS_WINDOWS */ +#endif /* HAVE_GETENTROPY */ /* Fill buffer with pseudo-random bytes generated by a linear congruent generator (LCG): @@ -242,6 +267,8 @@ _PyOS_URandom(void *buffer, Py_ssize_t size) #ifdef MS_WINDOWS return win32_urandom((unsigned char *)buffer, size, 1); +#elif HAVE_GETENTROPY + return py_getentropy(buffer, size, 0); #else return dev_urandom_python((char*)buffer, size); #endif @@ -287,6 +314,8 @@ _PyRandom_Init(void) else { #ifdef MS_WINDOWS (void)win32_urandom(secret, secret_size, 0); +#elif HAVE_GETENTROPY + (void)py_getentropy(secret, secret_size, 1); #else dev_urandom_noraise(secret, secret_size); #endif @@ -301,6 +330,8 @@ _PyRandom_Fini(void) CryptReleaseContext(hCryptProv, 0); hCryptProv = 0; } +#elif HAVE_GETENTROPY + /* nothing to clean */ #else dev_urandom_close(); #endif -- cgit v1.2.1 From 4b5c8edd294f02afef7407d9fd5236789bd4bc34 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Sat, 21 Feb 2015 08:44:05 -0800 Subject: Issue #23152: Implement _Py_fstat() to support files larger than 2 GB on Windows. fstat() may fail with EOVERFLOW on files larger than 2 GB because the file size type is an signed 32-bit integer. --- Python/random.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Python/random.c') diff --git a/Python/random.c b/Python/random.c index 93d300dae4..031d8875c9 100644 --- a/Python/random.c +++ b/Python/random.c @@ -139,14 +139,14 @@ dev_urandom_python(char *buffer, Py_ssize_t size) { int fd; Py_ssize_t n; - struct stat st; + struct _Py_stat_struct st; if (size <= 0) return 0; if (urandom_cache.fd >= 0) { /* Does the fd point to the same thing as before? (issue #21207) */ - if (fstat(urandom_cache.fd, &st) + if (_Py_fstat(urandom_cache.fd, &st) || st.st_dev != urandom_cache.st_dev || st.st_ino != urandom_cache.st_ino) { /* Something changed: forget the cached fd (but don't close it, @@ -178,7 +178,7 @@ dev_urandom_python(char *buffer, Py_ssize_t size) fd = urandom_cache.fd; } else { - if (fstat(fd, &st)) { + if (_Py_fstat(fd, &st)) { PyErr_SetFromErrno(PyExc_OSError); close(fd); return -1; -- cgit v1.2.1 From b8d0bcab60af86bb0d982ef0483eec7930a0a9d0 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 18 Mar 2015 00:22:14 +0100 Subject: Issue #23694: Enhance _Py_open(), it now raises exceptions * _Py_open() now raises exceptions on error. If open() fails, it raises an OSError with the filename. * _Py_open() now releases the GIL while calling open() * Add _Py_open_noraise() when _Py_open() cannot be used because the GIL is not held --- Python/random.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'Python/random.c') diff --git a/Python/random.c b/Python/random.c index 031d8875c9..39a389c79f 100644 --- a/Python/random.c +++ b/Python/random.c @@ -111,7 +111,7 @@ dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size) assert (0 < size); - fd = _Py_open("/dev/urandom", O_RDONLY); + fd = _Py_open_noraise("/dev/urandom", O_RDONLY); if (fd < 0) Py_FatalError("Failed to open /dev/urandom"); @@ -158,17 +158,13 @@ dev_urandom_python(char *buffer, Py_ssize_t size) if (urandom_cache.fd >= 0) fd = urandom_cache.fd; else { - Py_BEGIN_ALLOW_THREADS fd = _Py_open("/dev/urandom", O_RDONLY); - Py_END_ALLOW_THREADS - if (fd < 0) - { + if (fd < 0) { if (errno == ENOENT || errno == ENXIO || errno == ENODEV || errno == EACCES) PyErr_SetString(PyExc_NotImplementedError, "/dev/urandom (or equivalent) not found"); - else - PyErr_SetFromErrno(PyExc_OSError); + /* otherwise, keep the OSError exception raised by _Py_open() */ return -1; } if (urandom_cache.fd >= 0) { -- cgit v1.2.1 From cb2d306857151476ebbd659e3ef135d523455f23 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 18 Mar 2015 14:39:33 +0100 Subject: 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. --- Python/random.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 84 insertions(+), 6 deletions(-) (limited to 'Python/random.c') diff --git a/Python/random.c b/Python/random.c index 39a389c79f..dcc3aab79f 100644 --- a/Python/random.c +++ b/Python/random.c @@ -1,11 +1,17 @@ #include "Python.h" #ifdef MS_WINDOWS -#include +# include #else -#include -#ifdef HAVE_SYS_STAT_H -#include -#endif +# include +# ifdef HAVE_SYS_STAT_H +# include +# endif +# ifdef HAVE_SYS_SYSCALL_H +# include +# 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) -- cgit v1.2.1 From 82ab525ae487e6f735821276641415ab58213fd6 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 19 Mar 2015 22:21:49 +0100 Subject: Issue #22181: The availability of the getrandom() is now checked in configure, and stored in pyconfig.h as the new HAVE_GETRANDOM_SYSCALL define. Fix os.urandom() tests using file descriptors if os.urandom() uses getrandom(). --- Python/random.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'Python/random.c') diff --git a/Python/random.c b/Python/random.c index dcc3aab79f..10fc505e0f 100644 --- a/Python/random.c +++ b/Python/random.c @@ -6,11 +6,8 @@ # ifdef HAVE_SYS_STAT_H # include # endif -# ifdef HAVE_SYS_SYSCALL_H +# ifdef HAVE_GETRANDOM_SYSCALL # include -# if defined(__linux__) && defined(SYS_getrandom) -# define HAVE_GETRANDOM -# endif # endif #endif @@ -102,7 +99,7 @@ py_getentropy(unsigned char *buffer, Py_ssize_t size, int fatal) #else /* !HAVE_GETENTROPY */ -#ifdef HAVE_GETRANDOM +#ifdef HAVE_GETRANDOM_SYSCALL static int py_getrandom(void *buffer, Py_ssize_t size, int raise) { @@ -173,7 +170,7 @@ dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size) if (fd < 0) Py_FatalError("Failed to open /dev/urandom"); -#ifdef HAVE_GETRANDOM +#ifdef HAVE_GETRANDOM_SYSCALL if (py_getrandom(buffer, size, 0) == 1) return; /* getrandom() is not supported by the running kernel, fall back @@ -205,14 +202,14 @@ dev_urandom_python(char *buffer, Py_ssize_t size) int fd; Py_ssize_t n; struct _Py_stat_struct st; -#ifdef HAVE_GETRANDOM +#ifdef HAVE_GETRANDOM_SYSCALL int res; #endif if (size <= 0) return 0; -#ifdef HAVE_GETRANDOM +#ifdef HAVE_GETRANDOM_SYSCALL res = py_getrandom(buffer, size, 1); if (res < 0) return -1; -- cgit v1.2.1 From 0f1b4c6827901688b24a202f9f93c25efd5d145f Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 19 Mar 2015 23:24:45 +0100 Subject: Issue #22181: Fix dev_urandom_noraise(), try calling py_getrandom() before opening /dev/urandom. --- Python/random.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'Python/random.c') diff --git a/Python/random.c b/Python/random.c index 10fc505e0f..c924323fda 100644 --- a/Python/random.c +++ b/Python/random.c @@ -166,10 +166,6 @@ dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size) assert (0 < size); - fd = _Py_open_noraise("/dev/urandom", O_RDONLY); - if (fd < 0) - Py_FatalError("Failed to open /dev/urandom"); - #ifdef HAVE_GETRANDOM_SYSCALL if (py_getrandom(buffer, size, 0) == 1) return; @@ -177,6 +173,10 @@ dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size) * on reading /dev/urandom */ #endif + fd = _Py_open_noraise("/dev/urandom", O_RDONLY); + if (fd < 0) + Py_FatalError("Failed to open /dev/urandom"); + while (0 < size) { do { -- cgit v1.2.1 From b63df00ad9222e7a31bf2fc6fba43183a41dc841 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 19 Mar 2015 23:36:33 +0100 Subject: Issue #23707: On UNIX, os.urandom() now calls the Python signal handler when read() is interrupted by a signal. dev_urandom_python() now calls _Py_read() helper instead of calling directly read(). --- Python/random.c | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) (limited to 'Python/random.c') diff --git a/Python/random.c b/Python/random.c index c924323fda..a281829f97 100644 --- a/Python/random.c +++ b/Python/random.c @@ -262,29 +262,21 @@ dev_urandom_python(char *buffer, Py_ssize_t size) } } - Py_BEGIN_ALLOW_THREADS do { - do { - n = read(fd, buffer, (size_t)size); - } while (n < 0 && errno == EINTR); - if (n <= 0) - break; + n = _Py_read(fd, buffer, (size_t)size); + if (n == -1) + return -1; + if (n == 0) { + PyErr_Format(PyExc_RuntimeError, + "Failed to read %zi bytes from /dev/urandom", + size); + return -1; + } + buffer += n; - size -= (Py_ssize_t)n; + size -= n; } while (0 < size); - Py_END_ALLOW_THREADS - if (n <= 0) - { - /* stop on error or if read(size) returned 0 */ - if (n < 0) - PyErr_SetFromErrno(PyExc_OSError); - else - PyErr_Format(PyExc_RuntimeError, - "Failed to read %zi bytes from /dev/urandom", - size); - return -1; - } return 0; } -- cgit v1.2.1 From 0600ebc4d9a0fbb0f16689fb2c1d6a09482594cc Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 30 Mar 2015 10:09:31 +0200 Subject: Issue #23752: _Py_fstat() is now responsible to raise the Python exception Add _Py_fstat_noraise() function when a Python exception is not welcome. --- Python/random.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'Python/random.c') diff --git a/Python/random.c b/Python/random.c index a281829f97..a4eba3ccd9 100644 --- a/Python/random.c +++ b/Python/random.c @@ -221,7 +221,7 @@ dev_urandom_python(char *buffer, Py_ssize_t size) if (urandom_cache.fd >= 0) { /* Does the fd point to the same thing as before? (issue #21207) */ - if (_Py_fstat(urandom_cache.fd, &st) + if (_Py_fstat_noraise(urandom_cache.fd, &st) || st.st_dev != urandom_cache.st_dev || st.st_ino != urandom_cache.st_ino) { /* Something changed: forget the cached fd (but don't close it, @@ -250,7 +250,6 @@ dev_urandom_python(char *buffer, Py_ssize_t size) } else { if (_Py_fstat(fd, &st)) { - PyErr_SetFromErrno(PyExc_OSError); close(fd); return -1; } -- cgit v1.2.1 From 5f1c711bdaea4c383659129dd1563eb8c2de306f Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 30 Mar 2015 11:16:40 +0200 Subject: Issue #22181: os.urandom() now releases the GIL when the getrandom() implementation is used. --- Python/random.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'Python/random.c') diff --git a/Python/random.c b/Python/random.c index a4eba3ccd9..f3a8ae552e 100644 --- a/Python/random.c +++ b/Python/random.c @@ -115,9 +115,18 @@ py_getrandom(void *buffer, Py_ssize_t size, int raise) while (0 < size) { errno = 0; - /* the libc doesn't expose getrandom() yet, see: + + /* Use syscall() because 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 (raise) { + Py_BEGIN_ALLOW_THREADS + n = syscall(SYS_getrandom, buffer, size, flags); + Py_END_ALLOW_THREADS + } + else { + n = syscall(SYS_getrandom, buffer, size, flags); + } + if (n < 0) { if (errno == ENOSYS) { getrandom_works = 0; -- cgit v1.2.1 From 2ea2e83efdeebbd1d8280f36547068bb452c5a2d Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 30 Jul 2015 10:13:52 +0200 Subject: py_getrandom(): getrandom() *can* return EINTR See the latest version of getrandom() manual page: http://man7.org/linux/man-pages/man2/getrandom.2.html#NOTES The behavior when a call to getrandom() that is blocked while reading from /dev/urandom is interrupted by a signal handler depends on the initialization state of the entropy buffer and on the request size, buflen. If the entropy is not yet initialized, then the call will fail with the EINTR error. If the entropy pool has been initialized and the request size is large (buflen > 256), the call either succeeds, returning a partially filled buffer, or fails with the error EINTR. If the entropy pool has been initialized and the request size is small (buflen <= 256), then getrandom() will not fail with EINTR. Instead, it will return all of the bytes that have been requested. Note: py_getrandom() calls getrandom() with flags=0. --- Python/random.c | 1 - 1 file changed, 1 deletion(-) (limited to 'Python/random.c') diff --git a/Python/random.c b/Python/random.c index 9c9d5057c9..ea09e84a7b 100644 --- a/Python/random.c +++ b/Python/random.c @@ -142,7 +142,6 @@ py_getrandom(void *buffer, Py_ssize_t size, int raise) } if (errno == EINTR) { - /* Note: EINTR should not occur with flags=0 */ if (PyErr_CheckSignals()) { if (!raise) Py_FatalError("getrandom() interrupted by a signal"); -- cgit v1.2.1 From f4e099852a666678e2d66fc0b446805da3107d65 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 1 Oct 2015 09:47:30 +0200 Subject: Issue #25003: On Solaris 11.3 or newer, os.urandom() now uses the getrandom() function instead of the getentropy() function. The getentropy() function is blocking to generate very good quality entropy, os.urandom() doesn't need such high-quality entropy. --- Python/random.c | 49 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 15 deletions(-) (limited to 'Python/random.c') diff --git a/Python/random.c b/Python/random.c index ea09e84a7b..1d57b1b662 100644 --- a/Python/random.c +++ b/Python/random.c @@ -6,7 +6,9 @@ # ifdef HAVE_SYS_STAT_H # include # endif -# ifdef HAVE_GETRANDOM_SYSCALL +# ifdef HAVE_GETRANDOM +# include +# elif defined(HAVE_GETRANDOM_SYSCALL) # include # endif #endif @@ -70,7 +72,9 @@ win32_urandom(unsigned char *buffer, Py_ssize_t size, int raise) return 0; } -#elif HAVE_GETENTROPY +#elif defined(HAVE_GETENTROPY) && !defined(sun) +#define PY_GETENTROPY 1 + /* Fill buffer with size pseudo-random bytes generated by getentropy(). Return 0 on success, or raise an exception and return -1 on error. @@ -105,16 +109,19 @@ py_getentropy(unsigned char *buffer, Py_ssize_t size, int fatal) return 0; } -#else /* !HAVE_GETENTROPY */ +#else + +#if defined(HAVE_GETRANDOM) || defined(HAVE_GETRANDOM_SYSCALL) +#define PY_GETRANDOM 1 -#ifdef HAVE_GETRANDOM_SYSCALL 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 */ + /* 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 /dev/urandom, block if the kernel has no entropy */ + /* 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; int n; @@ -124,7 +131,18 @@ py_getrandom(void *buffer, Py_ssize_t size, int raise) while (0 < size) { errno = 0; - /* Use syscall() because the libc doesn't expose getrandom() yet, see: +#ifdef HAVE_GETRANDOM + if (raise) { + Py_BEGIN_ALLOW_THREADS + n = getrandom(buffer, size, flags); + Py_END_ALLOW_THREADS + } + else { + n = getrandom(buffer, size, flags); + } +#else + /* On Linux, use the syscall() function because the GNU libc doesn't + * expose the Linux getrandom() syscall yet. See: * https://sourceware.org/bugzilla/show_bug.cgi?id=17252 */ if (raise) { Py_BEGIN_ALLOW_THREADS @@ -134,6 +152,7 @@ py_getrandom(void *buffer, Py_ssize_t size, int raise) else { n = syscall(SYS_getrandom, buffer, size, flags); } +#endif if (n < 0) { if (errno == ENOSYS) { @@ -182,7 +201,7 @@ dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size) assert (0 < size); -#ifdef HAVE_GETRANDOM_SYSCALL +#ifdef PY_GETRANDOM if (py_getrandom(buffer, size, 0) == 1) return; /* getrandom() is not supported by the running kernel, fall back @@ -218,14 +237,14 @@ dev_urandom_python(char *buffer, Py_ssize_t size) int fd; Py_ssize_t n; struct _Py_stat_struct st; -#ifdef HAVE_GETRANDOM_SYSCALL +#ifdef PY_GETRANDOM int res; #endif if (size <= 0) return 0; -#ifdef HAVE_GETRANDOM_SYSCALL +#ifdef PY_GETRANDOM res = py_getrandom(buffer, size, 1); if (res < 0) return -1; @@ -304,7 +323,7 @@ dev_urandom_close(void) } } -#endif /* HAVE_GETENTROPY */ +#endif /* Fill buffer with pseudo-random bytes generated by a linear congruent generator (LCG): @@ -345,7 +364,7 @@ _PyOS_URandom(void *buffer, Py_ssize_t size) #ifdef MS_WINDOWS return win32_urandom((unsigned char *)buffer, size, 1); -#elif HAVE_GETENTROPY +#elif defined(PY_GETENTROPY) return py_getentropy(buffer, size, 0); #else return dev_urandom_python((char*)buffer, size); @@ -392,7 +411,7 @@ _PyRandom_Init(void) else { #ifdef MS_WINDOWS (void)win32_urandom(secret, secret_size, 0); -#elif HAVE_GETENTROPY +#elif defined(PY_GETENTROPY) (void)py_getentropy(secret, secret_size, 1); #else dev_urandom_noraise(secret, secret_size); @@ -408,7 +427,7 @@ _PyRandom_Fini(void) CryptReleaseContext(hCryptProv, 0); hCryptProv = 0; } -#elif HAVE_GETENTROPY +#elif defined(PY_GETENTROPY) /* nothing to clean */ #else dev_urandom_close(); -- cgit v1.2.1 From d0c43bc48b43531dd52811abbf2866cea5931073 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 12 Apr 2016 22:28:49 +0200 Subject: Fix os.urandom() on Solaris 11.3 Issue #26735: Fix os.urandom() on Solaris 11.3 and newer when reading more than 1,024 bytes: call getrandom() multiple times with a limit of 1024 bytes per call. --- Python/random.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'Python/random.c') diff --git a/Python/random.c b/Python/random.c index 772bfef0e8..79157b8b6f 100644 --- a/Python/random.c +++ b/Python/random.c @@ -131,16 +131,23 @@ py_getrandom(void *buffer, Py_ssize_t size, int raise) return 0; while (0 < size) { - errno = 0; +#ifdef sun + /* Issue #26735: On Solaris, getrandom() is limited to returning up + to 1024 bytes */ + n = Py_MIN(size, 1024); +#else + n = size; +#endif + errno = 0; #ifdef HAVE_GETRANDOM if (raise) { Py_BEGIN_ALLOW_THREADS - n = getrandom(buffer, size, flags); + n = getrandom(buffer, n, flags); Py_END_ALLOW_THREADS } else { - n = getrandom(buffer, size, flags); + n = getrandom(buffer, n, flags); } #else /* On Linux, use the syscall() function because the GNU libc doesn't @@ -148,11 +155,11 @@ py_getrandom(void *buffer, Py_ssize_t size, int raise) * https://sourceware.org/bugzilla/show_bug.cgi?id=17252 */ if (raise) { Py_BEGIN_ALLOW_THREADS - n = syscall(SYS_getrandom, buffer, size, flags); + n = syscall(SYS_getrandom, buffer, n, flags); Py_END_ALLOW_THREADS } else { - n = syscall(SYS_getrandom, buffer, size, flags); + n = syscall(SYS_getrandom, buffer, n, flags); } #endif -- cgit v1.2.1 From 55d6382677d025bd8dc2978d11df8a942406ec1f Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 7 Jun 2016 11:21:42 +0200 Subject: 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. --- Python/random.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) (limited to 'Python/random.c') diff --git a/Python/random.c b/Python/random.c index 79157b8b6f..ecfd44bbda 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()) { -- cgit v1.2.1 From 7938606e94f36bdcb5873c5d8fa380878b97a780 Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Fri, 10 Jun 2016 08:07:11 +0000 Subject: Fix typo and move comment to appropriate condition --- Python/random.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Python/random.c') diff --git a/Python/random.c b/Python/random.c index ecfd44bbda..07dacfe188 100644 --- a/Python/random.c +++ b/Python/random.c @@ -75,6 +75,8 @@ win32_urandom(unsigned char *buffer, Py_ssize_t size, int raise) return 0; } +/* Issue #25003: Don't use getentropy() on Solaris (available since + * Solaris 11.3), it is blocking whereas os.urandom() should not block. */ #elif defined(HAVE_GETENTROPY) && !defined(sun) #define PY_GETENTROPY 1 @@ -114,8 +116,6 @@ py_getentropy(unsigned char *buffer, Py_ssize_t size, int fatal) #else -/* Issue #25003: Don' use getentropy() on Solaris (available since - * Solaris 11.3), it is blocking whereas os.urandom() should not block. */ #if defined(HAVE_GETRANDOM) || defined(HAVE_GETRANDOM_SYSCALL) #define PY_GETRANDOM 1 -- cgit v1.2.1 From 2a8684b783b2cc31e76c6929d5c263ab370aeae1 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 14 Jun 2016 16:31:35 +0200 Subject: Fix os.urandom() using getrandom() on Linux Issue #27278: Fix os.urandom() implementation using getrandom() on Linux. Truncate size to INT_MAX and loop until we collected enough random bytes, instead of casting a directly Py_ssize_t to int. --- Python/random.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Python/random.c') diff --git a/Python/random.c b/Python/random.c index 07dacfe188..b9610208f6 100644 --- a/Python/random.c +++ b/Python/random.c @@ -143,7 +143,7 @@ py_getrandom(void *buffer, Py_ssize_t size, int raise) to 1024 bytes */ n = Py_MIN(size, 1024); #else - n = size; + n = Py_MIN(size, INT_MAX); #endif errno = 0; -- cgit v1.2.1 From 2ea476f7c097d3affc80f89ad4bfa32cc765d423 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 14 Jun 2016 16:35:49 +0200 Subject: cleanup random.c Casting Py_ssize_t to Py_ssize_t is useless. --- Python/random.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Python/random.c') diff --git a/Python/random.c b/Python/random.c index b9610208f6..8ce0b3edf6 100644 --- a/Python/random.c +++ b/Python/random.c @@ -251,7 +251,7 @@ dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size) break; } buffer += n; - size -= (Py_ssize_t)n; + size -= n; } close(fd); } -- cgit v1.2.1 From 8b2b944d78f4cc8fac52d8ed142a11865deac265 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 16 Jun 2016 23:53:47 +0200 Subject: py_getrandom(): use long type for the syscall() result Issue #27278. It should fix a conversion warning. In practice, the Linux kernel doesn't return more than 32 MB per call to the getrandom() syscall. --- Python/random.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Python/random.c') diff --git a/Python/random.c b/Python/random.c index 8ce0b3edf6..3119872abd 100644 --- a/Python/random.c +++ b/Python/random.c @@ -132,7 +132,7 @@ py_getrandom(void *buffer, Py_ssize_t size, int raise) * see https://bugs.python.org/issue26839. To avoid this, use the * GRND_NONBLOCK flag. */ const int flags = GRND_NONBLOCK; - int n; + long n; if (!getrandom_works) return 0; @@ -143,7 +143,7 @@ py_getrandom(void *buffer, Py_ssize_t size, int raise) to 1024 bytes */ n = Py_MIN(size, 1024); #else - n = Py_MIN(size, INT_MAX); + n = Py_MIN(size, LONG_MAX); #endif errno = 0; -- cgit v1.2.1 From 175e2c45acfc016887c59340436d9f5be0b073a7 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 20 Sep 2016 22:26:18 +0200 Subject: Cleanup random.c Issue #27955: modify py_getrnadom() and dev_urandom() * Add comments from Python 3.7 * PEP 7 style: add {...} --- Python/random.c | 81 +++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 53 insertions(+), 28 deletions(-) (limited to 'Python/random.c') diff --git a/Python/random.c b/Python/random.c index 3119872abd..5582e39714 100644 --- a/Python/random.c +++ b/Python/random.c @@ -119,11 +119,20 @@ py_getentropy(unsigned char *buffer, Py_ssize_t size, int fatal) #if defined(HAVE_GETRANDOM) || defined(HAVE_GETRANDOM_SYSCALL) #define PY_GETRANDOM 1 +/* Call getrandom() + - Return 1 on success + - Return 0 if getrandom() syscall is not available (failed with ENOSYS) + or if getrandom(GRND_NONBLOCK) failed with EAGAIN (system urandom + not initialized yet) and raise=0. + - Raise an exception (if raise is non-zero) and return -1 on error: + getrandom() failed with EINTR and the Python signal handler raised an + exception, or getrandom() failed with a different error. */ 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 newer, or Solaris 11.3 or newer */ + /* Is getrandom() supported by the running kernel? Set to 0 if getrandom() + failed with ENOSYS. Need Linux kernel 3.17 or newer, or Solaris + 11.3 or newer */ static int getrandom_works = 1; /* getrandom() on Linux will block if called before the kernel has @@ -134,8 +143,9 @@ py_getrandom(void *buffer, Py_ssize_t size, int raise) const int flags = GRND_NONBLOCK; long n; - if (!getrandom_works) + if (!getrandom_works) { return 0; + } while (0 < size) { #ifdef sun @@ -171,36 +181,42 @@ py_getrandom(void *buffer, Py_ssize_t size, int raise) #endif if (n < 0) { + /* ENOSYS: getrandom() syscall not supported by the kernel (but + * maybe supported by the host which built Python). */ if (errno == ENOSYS) { 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(GRND_NONBLOCK) fails with EAGAIN if the system + urandom is not initialiazed yet. In this case, fall back on + 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()) { - if (!raise) + if (!raise) { Py_FatalError("getrandom() interrupted by a signal"); + } return -1; } + /* retry getrandom() */ continue; } - if (raise) + if (raise) { PyErr_SetFromErrno(PyExc_OSError); - else + } + else { Py_FatalError("getrandom() failed"); + } return -1; } @@ -218,7 +234,9 @@ static struct { } urandom_cache = { -1 }; -/* Read size bytes from /dev/urandom into buffer. +/* Read 'size' random bytes from py_getrandom(). Fall back on reading from + /dev/urandom if getrandom() is not available. + Call Py_FatalError() on error. */ static void dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size) @@ -229,24 +247,26 @@ dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size) assert (0 < size); #ifdef PY_GETRANDOM - if (py_getrandom(buffer, size, 0) == 1) + if (py_getrandom(buffer, size, 0) == 1) { return; - /* getrandom() is not supported by the running kernel, fall back - * on reading /dev/urandom */ + } + /* getrandom() failed with ENOSYS, + fall back on reading /dev/urandom */ #endif fd = _Py_open_noraise("/dev/urandom", O_RDONLY); - if (fd < 0) + if (fd < 0) { Py_FatalError("Failed to open /dev/urandom"); + } while (0 < size) { do { n = read(fd, buffer, (size_t)size); } while (n < 0 && errno == EINTR); - if (n <= 0) - { - /* stop on error or if read(size) returned 0 */ + + if (n <= 0) { + /* read() failed or returned 0 bytes */ Py_FatalError("Failed to read bytes from /dev/urandom"); break; } @@ -256,8 +276,10 @@ dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size) close(fd); } -/* Read size bytes from /dev/urandom into buffer. - Return 0 on success, raise an exception and return -1 on error. */ +/* Read 'size' random bytes from py_getrandom(). Fall back on reading from + /dev/urandom if getrandom() is not available. + + Return 0 on success. Raise an exception and return -1 on error. */ static int dev_urandom_python(char *buffer, Py_ssize_t size) { @@ -273,12 +295,14 @@ dev_urandom_python(char *buffer, Py_ssize_t size) #ifdef PY_GETRANDOM res = py_getrandom(buffer, size, 1); - if (res < 0) + if (res < 0) { return -1; - if (res == 1) + } + if (res == 1) { return 0; - /* getrandom() is not supported by the running kernel, fall back - * on reading /dev/urandom */ + } + /* getrandom() failed with ENOSYS, + fall back on reading /dev/urandom */ #endif if (urandom_cache.fd >= 0) { @@ -325,8 +349,9 @@ dev_urandom_python(char *buffer, Py_ssize_t size) do { n = _Py_read(fd, buffer, (size_t)size); - if (n == -1) + if (n == -1) { return -1; + } if (n == 0) { PyErr_Format(PyExc_RuntimeError, "Failed to read %zi bytes from /dev/urandom", -- cgit v1.2.1 From 2e0a7b287c4b787328fceca616bec87963b6610e Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 20 Sep 2016 22:46:02 +0200 Subject: Catch EPERM error in py_getrandom() Issue #27955: Fallback on reading /dev/urandom device when the getrandom() syscall fails with EPERM, for example when blocked by SECCOMP. --- Python/random.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'Python/random.c') diff --git a/Python/random.c b/Python/random.c index 5582e39714..f2ada5f0d8 100644 --- a/Python/random.c +++ b/Python/random.c @@ -121,8 +121,8 @@ py_getentropy(unsigned char *buffer, Py_ssize_t size, int fatal) /* Call getrandom() - Return 1 on success - - Return 0 if getrandom() syscall is not available (failed with ENOSYS) - or if getrandom(GRND_NONBLOCK) failed with EAGAIN (system urandom + - Return 0 if getrandom() syscall is not available (failed with ENOSYS or + EPERM) or if getrandom(GRND_NONBLOCK) failed with EAGAIN (system urandom not initialized yet) and raise=0. - Raise an exception (if raise is non-zero) and return -1 on error: getrandom() failed with EINTR and the Python signal handler raised an @@ -131,7 +131,7 @@ static int py_getrandom(void *buffer, Py_ssize_t size, int raise) { /* Is getrandom() supported by the running kernel? Set to 0 if getrandom() - failed with ENOSYS. Need Linux kernel 3.17 or newer, or Solaris + failed with ENOSYS or EPERM. Need Linux kernel 3.17 or newer, or Solaris 11.3 or newer */ static int getrandom_works = 1; @@ -182,8 +182,9 @@ py_getrandom(void *buffer, Py_ssize_t size, int raise) if (n < 0) { /* ENOSYS: getrandom() syscall not supported by the kernel (but - * maybe supported by the host which built Python). */ - if (errno == ENOSYS) { + * maybe supported by the host which built Python). EPERM: + * getrandom() syscall blocked by SECCOMP or something else. */ + if (errno == ENOSYS || errno == EPERM) { getrandom_works = 0; return 0; } @@ -250,7 +251,7 @@ dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size) if (py_getrandom(buffer, size, 0) == 1) { return; } - /* getrandom() failed with ENOSYS, + /* getrandom() failed with ENOSYS or EPERM, fall back on reading /dev/urandom */ #endif @@ -301,7 +302,7 @@ dev_urandom_python(char *buffer, Py_ssize_t size) if (res == 1) { return 0; } - /* getrandom() failed with ENOSYS, + /* getrandom() failed with ENOSYS or EPERM, fall back on reading /dev/urandom */ #endif -- cgit v1.2.1