-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Add support for getrandom(2), add type check on file descriptor #1513
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,6 +30,9 @@ | |
#if PHP_WIN32 | ||
# include "win32/winutil.h" | ||
#endif | ||
#ifdef __linux__ | ||
# include <linux/random.h> | ||
#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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm digging the ascii art comments! |
||
|
||
*/ | ||
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; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we really want to have this check here? Performing a stat for every single random number generated seems very dubious to me. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doing it every read might not be necessary, but it's conservative. If we can say with 100% certainty that there is no operating system on Earth that would let the file descriptor stick open while the device was unlinked and an non-character device was installed in its place, even with a ring0 exploit, then this is not necessary. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I get what you're trying to do here, I just don't think it's worth it. I'm okay with doing a sanity check when acquiring the FD, but I don't want to have a significant slowdown by repeating it every time. Knowing that it is a chardev doesn't tell you much anyway -- after all, /dev/zero is a chardev as well. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right, I remember reading a way to sanity check that a while back, but can't find the reference. I'll update the PR as soon as I find it again. |
||
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; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although we mainly do this on Windows, but declarations at first but I'm not sure there is any recent (and sane) compiler that will complain here, but not a biggie.