]> granicus.if.org Git - python/commitdiff
(Merge 3.5) Catch EPERM error in py_getrandom()
authorVictor Stinner <victor.stinner@gmail.com>
Tue, 20 Sep 2016 20:49:52 +0000 (22:49 +0200)
committerVictor Stinner <victor.stinner@gmail.com>
Tue, 20 Sep 2016 20:49:52 +0000 (22:49 +0200)
Issue #27955: Fallback on reading /dev/urandom device when the getrandom()
syscall fails with EPERM, for example when blocked by SECCOMP.

1  2 
Misc/NEWS
Python/random.c

diff --cc Misc/NEWS
index 0a021ea15577d32e73bc7571c89d3d96bb744952,f5efee1d1adb3ee4ce8737ac44c6c8cfb1536826..97f669d66522520ab48758bb4c9df5e86090f2b9
+++ b/Misc/NEWS
@@@ -10,10 -10,9 +10,13 @@@ What's New in Python 3.6.0 beta 
  Core and Builtins
  -----------------
  
+ - Issue #27955: Fallback on reading /dev/urandom device when the getrandom()
+   syscall fails with EPERM, for example when blocked by SECCOMP.
 +- Issue #28192: Don't import readline in isolated mode.
 +
 +- Upgrade internal unicode databases to Unicode version 9.0.0.
 +
  - Issue #28131: Fix a regression in zipimport's compile_source().  zipimport
    should use the same optimization level as the interpreter.
  
diff --cc Python/random.c
index ea506eeeb236e2b9aceb5604efa58d84eeb448d1,f2ada5f0d8438dbd88f51be49ea4f0634cc97761..6f9f7110742df9827d5eff67f4d872e509e6b824
@@@ -128,14 -128,19 +128,14 @@@ py_getentropy(char *buffer, Py_ssize_t 
       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)
 +py_getrandom(void *buffer, Py_ssize_t size, int blocking, int raise)
  {
      /* Is getrandom() supported by the running kernel? Set to 0 if getrandom()
-        fails with ENOSYS. Need Linux kernel 3.17 or newer, or Solaris 11.3
-        or newer */
+        failed with ENOSYS or EPERM. 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
 -     * 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 flags;
 +    char *dest;
      long n;
  
      if (!getrandom_works) {
@@@ -246,74 -302,61 +249,74 @@@ dev_urandom(char *buffer, Py_ssize_t si
      if (res == 1) {
          return 0;
      }
-     /* getrandom() is not supported by the running kernel, fall back
-        on reading /dev/urandom */
+     /* getrandom() failed with ENOSYS or EPERM,
+        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_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,
 -               since it probably points to something important for some
 -               third-party code). */
 -            urandom_cache.fd = -1;
 -        }
 -    }
 -    if (urandom_cache.fd >= 0)
 -        fd = urandom_cache.fd;
 -    else {
 -        fd = _Py_open("/dev/urandom", O_RDONLY);
 -        if (fd < 0) {
 -            if (errno == ENOENT || errno == ENXIO ||
 -                errno == ENODEV || errno == EACCES)
 -                PyErr_SetString(PyExc_NotImplementedError,
 -                                "/dev/urandom (or equivalent) not found");
 -            /* otherwise, keep the OSError exception raised by _Py_open() */
 -            return -1;
 -        }
 +
 +    if (raise) {
 +        struct _Py_stat_struct st;
 +
          if (urandom_cache.fd >= 0) {
 -            /* urandom_fd was initialized by another thread while we were
 -               not holding the GIL, keep it. */
 -            close(fd);
 -            fd = urandom_cache.fd;
 +            /* Does the fd point to the same thing as before? (issue #21207) */
 +            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,
 +                   since it probably points to something important for some
 +                   third-party code). */
 +                urandom_cache.fd = -1;
 +            }
          }
 +        if (urandom_cache.fd >= 0)
 +            fd = urandom_cache.fd;
          else {
 -            if (_Py_fstat(fd, &st)) {
 -                close(fd);
 +            fd = _Py_open("/dev/urandom", O_RDONLY);
 +            if (fd < 0) {
 +                if (errno == ENOENT || errno == ENXIO ||
 +                    errno == ENODEV || errno == EACCES)
 +                    PyErr_SetString(PyExc_NotImplementedError,
 +                                    "/dev/urandom (or equivalent) not found");
 +                /* otherwise, keep the OSError exception raised by _Py_open() */
                  return -1;
              }
 +            if (urandom_cache.fd >= 0) {
 +                /* urandom_fd was initialized by another thread while we were
 +                   not holding the GIL, keep it. */
 +                close(fd);
 +                fd = urandom_cache.fd;
 +            }
              else {
 -                urandom_cache.fd = fd;
 -                urandom_cache.st_dev = st.st_dev;
 -                urandom_cache.st_ino = st.st_ino;
 +                if (_Py_fstat(fd, &st)) {
 +                    close(fd);
 +                    return -1;
 +                }
 +                else {
 +                    urandom_cache.fd = fd;
 +                    urandom_cache.st_dev = st.st_dev;
 +                    urandom_cache.st_ino = st.st_ino;
 +                }
              }
          }
 -    }
  
 -    do {
 -        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);
 +        do {
 +            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 -= n;
 +        } while (0 < size);
 +    }
 +    else {
 +        fd = _Py_open_noraise("/dev/urandom", O_RDONLY);
 +        if (fd < 0) {
              return -1;
          }