diff --git a/ext/standard/config.m4 b/ext/standard/config.m4 index c75b141c80dcc..c435f9655407e 100644 --- a/ext/standard/config.m4 +++ b/ext/standard/config.m4 @@ -597,6 +597,11 @@ dnl Check for arc4random on BSD systems dnl AC_CHECK_DECLS([arc4random_buf]) +dnl +dnl Check for getrandom on newer Linux kernels +dnl +AC_CHECK_DECLS([getrandom]) + dnl dnl Setup extension sources dnl diff --git a/ext/standard/random.c b/ext/standard/random.c index e2e48951cc0be..6d3ec141bcc84 100644 --- a/ext/standard/random.c +++ b/ext/standard/random.c @@ -30,6 +30,9 @@ #if PHP_WIN32 # include "win32/winutil.h" #endif +#ifdef __linux__ +# include +#endif #ifdef ZTS int random_globals_id; @@ -75,6 +78,7 @@ PHP_MSHUTDOWN_FUNCTION(random) /* }}} */ /* {{{ */ + static int php_random_bytes(void *bytes, size_t size) { #if PHP_WIN32 @@ -85,26 +89,67 @@ static int php_random_bytes(void *bytes, size_t size) } #elif HAVE_DECL_ARC4RANDOM_BUF arc4random_buf(bytes, size); +#elif HAVE_DECL_GETRANDOM + /* Linux getrandom(2) syscall */ + size_t read_bytes = 0; + size_t amount_to_read = 0; + ssize_t n; + + /* Keep reading until we get enough entropy */ + do { + amount_to_read = size - read_bytes; + /* Below, (bytes + read_bytes) is pointer arithmetic. + + bytes read_bytes size + | | | + [#######=============] (we're going to write over the = region) + \\\\\\\\\\\\\ + amount_to_read + + */ + n = getrandom(bytes + read_bytes, amount_to_read, 0); + + if (n == -1) { + if (errno == EINTR || errno == EAGAIN) { + /* Try again */ + continue; + } + /* + If the syscall fails, we are doomed. The loop that calls + php_random_bytes should be terminated by the exception instead + of proceeding to demand more entropy. + */ + zend_throw_exception(zend_ce_exception, "Could not gather sufficient random data", errno); + return FAILURE; + } + + 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; if (fd < 0) { -#if HAVE_DEV_ARANDOM - fd = open("/dev/arandom", O_RDONLY); -#elif HAVE_DEV_URANDOM +#if HAVE_DEV_URANDOM fd = open("/dev/urandom", O_RDONLY); #endif if (fd < 0) { zend_throw_exception(zend_ce_exception, "Cannot open source device", 0); return FAILURE; } - + /* Does the file exist and is it a character device? */ + if (fstat(fd, &st) != 0 || !S_ISCHR(st.st_mode)) { + close(fd); + zend_throw_exception(zend_ce_exception, "Error reading from source device", 0); + return FAILURE; + } RANDOM_G(fd) = fd; } while (read_bytes < size) { - ssize_t n = read(fd, bytes + read_bytes, size - read_bytes); + n = read(fd, bytes + read_bytes, size - read_bytes); if (n <= 0) { break; } diff --git a/ext/standard/tests/random/random_bytes.phpt b/ext/standard/tests/random/random_bytes.phpt index 86391383e443f..6a7d438835f95 100644 --- a/ext/standard/tests/random/random_bytes.phpt +++ b/ext/standard/tests/random/random_bytes.phpt @@ -8,7 +8,10 @@ var_dump(strlen(bin2hex(random_bytes(16)))); var_dump(is_string(random_bytes(10))); +var_dump(is_string(random_bytes(257))); + ?> --EXPECT-- int(32) bool(true) +bool(true)