Permalink
Browse files

Fixed bug #74105

If getrandom syscall is unavailable (ENOSYS), try to fallback on
/dev/urandom.
  • Loading branch information...
benjarobin authored and nikic committed Feb 15, 2017
1 parent 61e59db commit 513582814b0ca82d81eb6b98897d745e0f0eebf5
Showing with 47 additions and 41 deletions.
  1. +2 −0 NEWS
  2. +45 −41 ext/standard/random.c
2 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).
@@ -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

0 comments on commit 5135828

Please sign in to comment.