[Python-checkins] cpython: Issue #22181: On Linux, os.urandom() now uses the new getrandom() syscall if

victor.stinner python-checkins at python.org
Wed Mar 18 14:42:34 CET 2015


https://hg.python.org/cpython/rev/1fc32bf069ff
changeset:   95042:1fc32bf069ff
user:        Victor Stinner <victor.stinner at gmail.com>
date:        Wed Mar 18 14:39:33 2015 +0100
summary:
  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.

files:
  Misc/NEWS       |   5 ++
  Python/random.c |  90 ++++++++++++++++++++++++++++++++++--
  2 files changed, 89 insertions(+), 6 deletions(-)


diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -18,6 +18,11 @@
 Library
 -------
 
+- 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.
+
 - Issue #2211: Updated the implementation of the http.cookies.Morsel class.
   Setting attributes key, value and coded_value directly now is deprecated.
   update() and setdefault() now transform and check keys.  Comparing for
diff --git a/Python/random.c b/Python/random.c
--- 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 @@
     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 @@
     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 @@
     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)

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list