]> granicus.if.org Git - php/commitdiff
Fixed bug #74105
authorBenjamin Robin <dev@benjarobin.fr>
Wed, 15 Feb 2017 23:06:46 +0000 (00:06 +0100)
committerNikita Popov <nikita.ppv@gmail.com>
Fri, 17 Feb 2017 17:42:35 +0000 (18:42 +0100)
If getrandom syscall is unavailable (ENOSYS), try to fallback on
/dev/urandom.

NEWS
ext/standard/random.c

diff --git a/NEWS b/NEWS
index 4c73dbfbe6141983a16687da61f6e07acbcaf84f..6f9c15ca134a7dd98669abbfb754897065191f9b 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -54,6 +54,8 @@ PHP                                                                        NEWS
     (Anatol)
   . Fixed bug #73118 (is_callable callable name reports misleading value for
     anonymous classes). (Adam Saponara)
+  . Fixed bug #74105 (PHP on Linux should use /dev/urandom when getrandom is
+    not available). (Benjamin Robin)
 
 - Streams:
   . Fixed bug #73496 (Invalid memory access in zend_inline_hash_func).
index 6d314c559e8d98bbbaa5eb2462a67c03e8a781b8..1434450039b8e2b8dbe72e7b5fa0968872fb2d44 100644 (file)
@@ -93,14 +93,13 @@ PHPAPI int php_random_bytes(void *bytes, size_t size, zend_bool should_throw)
        }
 #elif HAVE_DECL_ARC4RANDOM_BUF && ((defined(__OpenBSD__) && OpenBSD >= 201405) || (defined(__NetBSD__) && __NetBSD_Version__ >= 700000001))
        arc4random_buf(bytes, size);
-#elif defined(__linux__) && defined(SYS_getrandom)
-       /* Linux getrandom(2) syscall */
+#else
        size_t read_bytes = 0;
-       size_t amount_to_read = 0;
        ssize_t n;
-
+#if defined(__linux__) && defined(SYS_getrandom)
+       /* Linux getrandom(2) syscall */
        /* Keep reading until we get enough entropy */
-       do {
+       while (read_bytes < size) {
                /* Below, (bytes + read_bytes)  is pointer arithmetic.
 
                   bytes   read_bytes  size
@@ -110,11 +109,17 @@ PHPAPI int php_random_bytes(void *bytes, size_t size, zend_bool should_throw)
                              amount_to_read
 
                */
-               amount_to_read = size - read_bytes;
+               size_t amount_to_read = size - read_bytes;
                n = syscall(SYS_getrandom, bytes + read_bytes, amount_to_read, 0);
 
                if (n == -1) {
-                       if (errno == EINTR || errno == EAGAIN) {
+                       if (errno == ENOSYS) {
+                               /* This can happen if PHP was compiled against a newer kernel where getrandom()
+                                * is available, but then runs on an older kernel without getrandom(). If this
+                                * happens we simply fall back to reading from /dev/urandom. */
+                               ZEND_ASSERT(read_bytes == 0);
+                               break;
+                       } else if (errno == EINTR || errno == EAGAIN) {
                                /* Try again */
                                continue;
                        }
@@ -130,53 +135,52 @@ PHPAPI int php_random_bytes(void *bytes, size_t size, zend_bool should_throw)
                }
 
                read_bytes += (size_t) n;
-       } while (read_bytes < size);
-#else
-       int    fd = RANDOM_G(fd);
-       struct stat st;
-       size_t read_bytes = 0;
-       ssize_t n;
+       }
+#endif
+       if (read_bytes < size) {
+               int    fd = RANDOM_G(fd);
+               struct stat st;
 
-       if (fd < 0) {
+               if (fd < 0) {
 #if HAVE_DEV_URANDOM
-               fd = open("/dev/urandom", O_RDONLY);
+                       fd = open("/dev/urandom", O_RDONLY);
 #endif
-               if (fd < 0) {
-                       if (should_throw) {
-                               zend_throw_exception(zend_ce_exception, "Cannot open source device", 0);
+                       if (fd < 0) {
+                               if (should_throw) {
+                                       zend_throw_exception(zend_ce_exception, "Cannot open source device", 0);
+                               }
+                               return FAILURE;
                        }
-                       return FAILURE;
-               }
-               /* Does the file exist and is it a character device? */
-               if (fstat(fd, &st) != 0 || 
+                       /* Does the file exist and is it a character device? */
+                       if (fstat(fd, &st) != 0 ||
 # ifdef S_ISNAM
-                !(S_ISNAM(st.st_mode) || S_ISCHR(st.st_mode))
+                                       !(S_ISNAM(st.st_mode) || S_ISCHR(st.st_mode))
 # else
-                !S_ISCHR(st.st_mode)
+                                       !S_ISCHR(st.st_mode)
 # endif
-               ) {
-                       close(fd);
-                       if (should_throw) {
-                               zend_throw_exception(zend_ce_exception, "Error reading from source device", 0);
+                       ) {
+                               close(fd);
+                               if (should_throw) {
+                                       zend_throw_exception(zend_ce_exception, "Error reading from source device", 0);
+                               }
+                               return FAILURE;
                        }
-                       return FAILURE;
+                       RANDOM_G(fd) = fd;
                }
-               RANDOM_G(fd) = fd;
-       }
 
-       while (read_bytes < size) {
-               n = read(fd, bytes + read_bytes, size - read_bytes);
-               if (n <= 0) {
-                       break;
+               for (read_bytes = 0; read_bytes < size; read_bytes += (size_t) n) {
+                       n = read(fd, bytes + read_bytes, size - read_bytes);
+                       if (n <= 0) {
+                               break;
+                       }
                }
-               read_bytes += n;
-       }
 
-       if (read_bytes < size) {
-               if (should_throw) {
-                       zend_throw_exception(zend_ce_exception, "Could not gather sufficient random data", 0);
+               if (read_bytes < size) {
+                       if (should_throw) {
+                               zend_throw_exception(zend_ce_exception, "Could not gather sufficient random data", 0);
+                       }
+                       return FAILURE;
                }
-               return FAILURE;
        }
 #endif