[Python-checkins] cpython: Issue #27776: Cleanup random.c

victor.stinner python-checkins at python.org
Tue Aug 16 09:24:59 EDT 2016


https://hg.python.org/cpython/rev/980e2c781810
changeset:   102699:980e2c781810
user:        Victor Stinner <victor.stinner at gmail.com>
date:        Tue Aug 16 15:23:58 2016 +0200
summary:
  Issue #27776: Cleanup random.c

* Add pyurandom() helper function to factorize the code
* don't call Py_FatalError() in helper functions, but only in _PyRandom_Init()
  if pyurandom() failed, to uniformize the code

files:
  Python/random.c |  133 ++++++++++++++++++++---------------
  1 files changed, 76 insertions(+), 57 deletions(-)


diff --git a/Python/random.c b/Python/random.c
--- a/Python/random.c
+++ b/Python/random.c
@@ -39,10 +39,9 @@
     return 0;
 
 error:
-    if (raise)
+    if (raise) {
         PyErr_SetFromWindowsErr(0);
-    else
-        Py_FatalError("Failed to initialize Windows random API (CryptoGen)");
+    }
     return -1;
 }
 
@@ -55,8 +54,9 @@
 
     if (hCryptProv == 0)
     {
-        if (win32_urandom_init(raise) == -1)
+        if (win32_urandom_init(raise) == -1) {
             return -1;
+        }
     }
 
     while (size > 0)
@@ -65,11 +65,9 @@
         if (!CryptGenRandom(hCryptProv, (DWORD)chunk, buffer))
         {
             /* CryptGenRandom() failed */
-            if (raise)
+            if (raise) {
                 PyErr_SetFromWindowsErr(0);
-            else
-                Py_FatalError("Failed to initialized the randomized hash "
-                        "secret using CryptoGen)");
+            }
             return -1;
         }
         buffer += chunk;
@@ -86,29 +84,28 @@
 /* Fill buffer with size pseudo-random bytes generated by getentropy().
    Return 0 on success, or raise an exception and return -1 on error.
 
-   If fatal is nonzero, call Py_FatalError() instead of raising an exception
-   on error. */
+   If raise is zero, don't raise an exception on error. */
 static int
-py_getentropy(unsigned char *buffer, Py_ssize_t size, int fatal)
+py_getentropy(char *buffer, Py_ssize_t size, int raise)
 {
     while (size > 0) {
         Py_ssize_t len = Py_MIN(size, 256);
         int res;
 
-        if (!fatal) {
+        if (raise) {
             Py_BEGIN_ALLOW_THREADS
             res = getentropy(buffer, len);
             Py_END_ALLOW_THREADS
-
-            if (res < 0) {
-                PyErr_SetFromErrno(PyExc_OSError);
-                return -1;
-            }
         }
         else {
             res = getentropy(buffer, len);
-            if (res < 0)
-                Py_FatalError("getentropy() failed");
+        }
+
+        if (res < 0) {
+            if (raise) {
+                PyErr_SetFromErrno(PyExc_OSError);
+            }
+            return -1;
         }
 
         buffer += len;
@@ -195,18 +192,15 @@
 
             if (errno == EINTR) {
                 if (PyErr_CheckSignals()) {
-                    if (!raise)
-                        Py_FatalError("getrandom() interrupted by a signal");
                     return -1;
                 }
                 /* retry getrandom() */
                 continue;
             }
 
-            if (raise)
+            if (raise) {
                 PyErr_SetFromErrno(PyExc_OSError);
-            else
-                Py_FatalError("getrandom() failed");
+            }
             return -1;
         }
 
@@ -225,9 +219,9 @@
 
 
 /* Read size bytes from /dev/urandom into buffer.
-   Call Py_FatalError() on error. */
-static void
-dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size)
+   Return 0 success, or return -1 on error. */
+static int
+dev_urandom_noraise(char *buffer, Py_ssize_t size)
 {
     int fd;
     Py_ssize_t n;
@@ -235,31 +229,35 @@
     assert (0 < size);
 
 #ifdef PY_GETRANDOM
-    if (py_getrandom(buffer, size, 0) == 1)
-        return;
+    if (py_getrandom(buffer, size, 0) == 1) {
+        return 0;
+    }
     /* getrandom() is not supported by the running kernel, fall back
      * on reading /dev/urandom */
 #endif
 
     fd = _Py_open_noraise("/dev/urandom", O_RDONLY);
-    if (fd < 0)
-        Py_FatalError("Failed to open /dev/urandom");
+    if (fd < 0) {
+        return -1;
+    }
 
     while (0 < size)
     {
         do {
             n = read(fd, buffer, (size_t)size);
         } while (n < 0 && errno == EINTR);
-        if (n <= 0)
-        {
+
+        if (n <= 0) {
             /* stop on error or if read(size) returned 0 */
-            Py_FatalError("Failed to read bytes from /dev/urandom");
-            break;
+            return -1;
         }
+
         buffer += n;
         size -= n;
     }
     close(fd);
+
+    return 0;
 }
 
 /* Read size bytes from /dev/urandom into buffer.
@@ -379,6 +377,40 @@
     }
 }
 
+/* If raise is zero:
+ * - Don't raise exceptions on error
+ * - Don't call PyErr_CheckSignals() on EINTR (retry directly the interrupted
+ *   syscall)
+ * - Don't release the GIL to call syscalls. */
+static int
+pyurandom(void *buffer, Py_ssize_t size, int raise)
+{
+    if (size < 0) {
+        if (raise) {
+            PyErr_Format(PyExc_ValueError,
+                         "negative argument not allowed");
+        }
+        return -1;
+    }
+
+    if (size == 0) {
+        return 0;
+    }
+
+#ifdef MS_WINDOWS
+    return win32_urandom((unsigned char *)buffer, size, raise);
+#elif defined(PY_GETENTROPY)
+    return py_getentropy(buffer, size, raise);
+#else
+    if (raise) {
+        return dev_urandom_python(buffer, size);
+    }
+    else {
+        return dev_urandom_noraise(buffer, size);
+    }
+#endif
+}
+
 /* Fill buffer with size pseudo-random bytes from the operating system random
    number generator (RNG). It is suitable for most cryptographic purposes
    except long living private keys for asymmetric encryption.
@@ -387,21 +419,7 @@
 int
 _PyOS_URandom(void *buffer, Py_ssize_t size)
 {
-    if (size < 0) {
-        PyErr_Format(PyExc_ValueError,
-                     "negative argument not allowed");
-        return -1;
-    }
-    if (size == 0)
-        return 0;
-
-#ifdef MS_WINDOWS
-    return win32_urandom((unsigned char *)buffer, size, 1);
-#elif defined(PY_GETENTROPY)
-    return py_getentropy(buffer, size, 0);
-#else
-    return dev_urandom_python((char*)buffer, size);
-#endif
+    return pyurandom(buffer, size, 1);
 }
 
 void
@@ -442,13 +460,14 @@
         }
     }
     else {
-#ifdef MS_WINDOWS
-        (void)win32_urandom(secret, secret_size, 0);
-#elif defined(PY_GETENTROPY)
-        (void)py_getentropy(secret, secret_size, 1);
-#else
-        dev_urandom_noraise(secret, secret_size);
-#endif
+        int res;
+
+        /* _PyRandom_Init() is called very early in the Python initialization
+         * and so exceptions cannot be used. */
+        res = pyurandom(secret, secret_size, 0);
+        if (res < 0) {
+            Py_FatalError("failed to get random numbers to initialize Python");
+        }
     }
 }
 

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


More information about the Python-checkins mailing list