]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Issue #27776: Cleanup random.c
authorVictor Stinner <victor.stinner@gmail.com>
Tue, 16 Aug 2016 13:23:58 +0000 (15:23 +0200)
committerVictor Stinner <victor.stinner@gmail.com>
Tue, 16 Aug 2016 13:23:58 +0000 (15:23 +0200)
* 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

Python/random.c

index 945269a2f6c84c68bc698b1d1cceae3dfaf4c954..7d37fe95753e8622f16860edd9ce97ee8487e4ff 100644 (file)
@@ -39,10 +39,9 @@ win32_urandom_init(int raise)
     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 @@ win32_urandom(unsigned char *buffer, Py_ssize_t size, int raise)
 
     if (hCryptProv == 0)
     {
-        if (win32_urandom_init(raise) == -1)
+        if (win32_urandom_init(raise) == -1) {
             return -1;
+        }
     }
 
     while (size > 0)
@@ -65,11 +65,9 @@ win32_urandom(unsigned char *buffer, Py_ssize_t size, int raise)
         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 @@ win32_urandom(unsigned char *buffer, Py_ssize_t size, int raise)
 /* 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 @@ py_getrandom(void *buffer, Py_ssize_t size, int raise)
 
             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 @@ static struct {
 
 
 /* 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 @@ dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size)
     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,31 +377,51 @@ lcg_urandom(unsigned int x0, unsigned char *buffer, size_t size)
     }
 }
 
-/* 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.
-
  Return 0 on success, raise an exception and return -1 on error. */
-int
-_PyOS_URandom(void *buffer, Py_ssize_t size)
+/* 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) {
-        PyErr_Format(PyExc_ValueError,
-                     "negative argument not allowed");
+        if (raise) {
+            PyErr_Format(PyExc_ValueError,
+                         "negative argument not allowed");
+        }
         return -1;
     }
-    if (size == 0)
+
+    if (size == 0) {
         return 0;
+    }
 
 #ifdef MS_WINDOWS
-    return win32_urandom((unsigned char *)buffer, size, 1);
+    return win32_urandom((unsigned char *)buffer, size, raise);
 #elif defined(PY_GETENTROPY)
-    return py_getentropy(buffer, size, 0);
+    return py_getentropy(buffer, size, raise);
 #else
-    return dev_urandom_python((char*)buffer, size);
+    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.
+
+   Return 0 on success, raise an exception and return -1 on error. */
+int
+_PyOS_URandom(void *buffer, Py_ssize_t size)
+{
+    return pyurandom(buffer, size, 1);
+}
+
 void
 _PyRandom_Init(void)
 {
@@ -442,13 +460,14 @@ _PyRandom_Init(void)
         }
     }
     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");
+        }
     }
 }