Skip to content

random: Make php_random_bytes() useable early during engine startup #14291

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

Merged
merged 2 commits into from
May 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Zend/zend_portability.h
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,16 @@ char *alloca();
# define ZEND_ATTRIBUTE_UNUSED
#endif

#if ZEND_GCC_VERSION >= 3003 || __has_attribute(nonnull)
/* All pointer arguments must be non-null */
# define ZEND_ATTRIBUTE_NONNULL __attribute__((nonnull))
/* Specified arguments must be non-null (1-based) */
# define ZEND_ATTRIBUTE_NONNULL_ARGS(...) __attribute__((nonnull(__VA_ARGS__)))
#else
# define ZEND_ATTRIBUTE_NONNULL
# define ZEND_ATTRIBUTE_NONNULL_ARGS(...)
#endif

#if defined(__GNUC__) && ZEND_GCC_VERSION >= 4003
# define ZEND_COLD __attribute__((cold))
# ifdef __OPTIMIZE__
Expand Down
78 changes: 50 additions & 28 deletions ext/random/csprng.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "php.h"

#include "Zend/zend_exceptions.h"
#include "Zend/zend_atomic.h"

#include "php_random.h"
#include "php_random_csprng.h"
Expand Down Expand Up @@ -61,14 +62,16 @@
# include <sanitizer/msan_interface.h>
#endif

PHPAPI zend_result php_random_bytes(void *bytes, size_t size, bool should_throw)
#ifndef PHP_WIN32
static zend_atomic_int random_fd = ZEND_ATOMIC_INT_INITIALIZER(-1);
#endif

ZEND_ATTRIBUTE_NONNULL PHPAPI zend_result php_random_bytes_ex(void *bytes, size_t size, char *errstr, size_t errstr_size)
{
#ifdef PHP_WIN32
/* Defer to CryptGenRandom on Windows */
if (php_win32_get_random_bytes(bytes, size) == FAILURE) {
if (should_throw) {
zend_throw_exception(random_ce_Random_RandomException, "Failed to retrieve randomness from the operating system (BCryptGenRandom)", 0);
}
snprintf(errstr, errstr_size, "Failed to retrieve randomness from the operating system (BCryptGenRandom)");
return FAILURE;
}
#elif HAVE_COMMONCRYPTO_COMMONRANDOM_H
Expand All @@ -79,9 +82,7 @@ PHPAPI zend_result php_random_bytes(void *bytes, size_t size, bool should_throw)
* the vast majority of the time, it works fine ; but better make sure we catch failures
*/
if (CCRandomGenerateBytes(bytes, size) != kCCSuccess) {
if (should_throw) {
zend_throw_exception(random_ce_Random_RandomException, "Failed to retrieve randomness from the operating system (CCRandomGenerateBytes)", 0);
}
snprintf(errstr, errstr_size, "Failed to retrieve randomness from the operating system (CCRandomGenerateBytes)");
return FAILURE;
}
#elif HAVE_DECL_ARC4RANDOM_BUF && ((defined(__OpenBSD__) && OpenBSD >= 201405) || (defined(__NetBSD__) && __NetBSD_Version__ >= 700000001 && __NetBSD_Version__ < 1000000000) || \
Expand Down Expand Up @@ -147,19 +148,17 @@ PHPAPI zend_result php_random_bytes(void *bytes, size_t size, bool should_throw)
}
# endif
if (read_bytes < size) {
int fd = RANDOM_G(random_fd);
int fd = zend_atomic_int_load_ex(&random_fd);
struct stat st;

if (fd < 0) {
errno = 0;
fd = open("/dev/urandom", O_RDONLY);
if (fd < 0) {
if (should_throw) {
if (errno != 0) {
zend_throw_exception_ex(random_ce_Random_RandomException, 0, "Cannot open /dev/urandom: %s", strerror(errno));
} else {
zend_throw_exception_ex(random_ce_Random_RandomException, 0, "Cannot open /dev/urandom");
}
if (errno != 0) {
snprintf(errstr, errstr_size, "Cannot open /dev/urandom: %s", strerror(errno));
} else {
snprintf(errstr, errstr_size, "Cannot open /dev/urandom");
}
return FAILURE;
}
Expand All @@ -174,16 +173,19 @@ PHPAPI zend_result php_random_bytes(void *bytes, size_t size, bool should_throw)
# endif
) {
close(fd);
if (should_throw) {
if (errno != 0) {
zend_throw_exception_ex(random_ce_Random_RandomException, 0, "Error reading from /dev/urandom: %s", strerror(errno));
} else {
zend_throw_exception_ex(random_ce_Random_RandomException, 0, "Error reading from /dev/urandom");
}
if (errno != 0) {
snprintf(errstr, errstr_size, "Error reading from /dev/urandom: %s", strerror(errno));
} else {
snprintf(errstr, errstr_size, "Error reading from /dev/urandom");
}
return FAILURE;
}
RANDOM_G(random_fd) = fd;
int expected = -1;
if (!zend_atomic_int_compare_exchange_ex(&random_fd, &expected, fd)) {
close(fd);
/* expected is now the actual value of random_fd */
fd = expected;
}
}

read_bytes = 0;
Expand All @@ -192,12 +194,10 @@ PHPAPI zend_result php_random_bytes(void *bytes, size_t size, bool should_throw)
ssize_t n = read(fd, bytes + read_bytes, size - read_bytes);

if (n <= 0) {
if (should_throw) {
if (errno != 0) {
zend_throw_exception_ex(random_ce_Random_RandomException, 0, "Could not gather sufficient random data: %s", strerror(errno));
} else {
zend_throw_exception_ex(random_ce_Random_RandomException, 0, "Could not gather sufficient random data");
}
if (errno != 0) {
snprintf(errstr, errstr_size, "Could not gather sufficient random data: %s", strerror(errno));
} else {
snprintf(errstr, errstr_size, "Could not gather sufficient random data");
}
return FAILURE;
}
Expand All @@ -210,7 +210,19 @@ PHPAPI zend_result php_random_bytes(void *bytes, size_t size, bool should_throw)
return SUCCESS;
}

PHPAPI zend_result php_random_int(zend_long min, zend_long max, zend_long *result, bool should_throw)
ZEND_ATTRIBUTE_NONNULL PHPAPI zend_result php_random_bytes(void *bytes, size_t size, bool should_throw)
{
char errstr[128];
zend_result result = php_random_bytes_ex(bytes, size, errstr, sizeof(errstr));

if (result == FAILURE && should_throw) {
zend_throw_exception(random_ce_Random_RandomException, errstr, 0);
}

return result;
}

ZEND_ATTRIBUTE_NONNULL PHPAPI zend_result php_random_int(zend_long min, zend_long max, zend_long *result, bool should_throw)
{
zend_ulong umax;
zend_ulong trial;
Expand Down Expand Up @@ -251,3 +263,13 @@ PHPAPI zend_result php_random_int(zend_long min, zend_long max, zend_long *resul
*result = (zend_long)((trial % umax) + min);
return SUCCESS;
}

PHPAPI void php_random_csprng_shutdown(void)
{
#ifndef PHP_WIN32
int fd = zend_atomic_int_exchange(&random_fd, -1);
if (fd != -1) {
close(fd);
}
#endif
}
1 change: 0 additions & 1 deletion ext/random/php_random.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,6 @@ PHP_MSHUTDOWN_FUNCTION(random);
PHP_RINIT_FUNCTION(random);

ZEND_BEGIN_MODULE_GLOBALS(random)
int random_fd;
bool combined_lcg_seeded;
bool mt19937_seeded;
bool fallback_seed_initialized;
Expand Down
16 changes: 10 additions & 6 deletions ext/random/php_random_csprng.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,27 +20,31 @@

# include "php.h"

PHPAPI zend_result php_random_bytes(void *bytes, size_t size, bool should_throw);
PHPAPI zend_result php_random_int(zend_long min, zend_long max, zend_long *result, bool should_throw);
ZEND_ATTRIBUTE_NONNULL PHPAPI zend_result php_random_bytes(void *bytes, size_t size, bool should_throw);
ZEND_ATTRIBUTE_NONNULL PHPAPI zend_result php_random_bytes_ex(void *bytes, size_t size, char *errstr, size_t errstr_size);

static inline zend_result php_random_bytes_throw(void *bytes, size_t size)
ZEND_ATTRIBUTE_NONNULL PHPAPI zend_result php_random_int(zend_long min, zend_long max, zend_long *result, bool should_throw);

ZEND_ATTRIBUTE_NONNULL static inline zend_result php_random_bytes_throw(void *bytes, size_t size)
{
return php_random_bytes(bytes, size, true);
}

static inline zend_result php_random_bytes_silent(void *bytes, size_t size)
ZEND_ATTRIBUTE_NONNULL static inline zend_result php_random_bytes_silent(void *bytes, size_t size)
{
return php_random_bytes(bytes, size, false);
}

static inline zend_result php_random_int_throw(zend_long min, zend_long max, zend_long *result)
ZEND_ATTRIBUTE_NONNULL static inline zend_result php_random_int_throw(zend_long min, zend_long max, zend_long *result)
{
return php_random_int(min, max, result, true);
}

static inline zend_result php_random_int_silent(zend_long min, zend_long max, zend_long *result)
ZEND_ATTRIBUTE_NONNULL static inline zend_result php_random_int_silent(zend_long min, zend_long max, zend_long *result)
{
return php_random_int(min, max, result, false);
}

PHPAPI void php_random_csprng_shutdown(void);

#endif /* PHP_RANDOM_CSPRNG_H */
24 changes: 11 additions & 13 deletions ext/random/random.c
Original file line number Diff line number Diff line change
Expand Up @@ -683,21 +683,10 @@ PHPAPI uint64_t php_random_generate_fallback_seed(void)
/* {{{ PHP_GINIT_FUNCTION */
static PHP_GINIT_FUNCTION(random)
{
random_globals->random_fd = -1;
random_globals->fallback_seed_initialized = false;
}
/* }}} */

/* {{{ PHP_GSHUTDOWN_FUNCTION */
static PHP_GSHUTDOWN_FUNCTION(random)
{
if (random_globals->random_fd >= 0) {
close(random_globals->random_fd);
random_globals->random_fd = -1;
}
}
/* }}} */

/* {{{ PHP_MINIT_FUNCTION */
PHP_MINIT_FUNCTION(random)
{
Expand Down Expand Up @@ -766,6 +755,15 @@ PHP_MINIT_FUNCTION(random)
}
/* }}} */

/* {{{ PHP_MSHUTDOWN_FUNCTION */
PHP_MSHUTDOWN_FUNCTION(random)
{
php_random_csprng_shutdown();

return SUCCESS;
}
/* }}} */

/* {{{ PHP_RINIT_FUNCTION */
PHP_RINIT_FUNCTION(random)
{
Expand All @@ -782,14 +780,14 @@ zend_module_entry random_module_entry = {
"random", /* Extension name */
ext_functions, /* zend_function_entry */
PHP_MINIT(random), /* PHP_MINIT - Module initialization */
NULL, /* PHP_MSHUTDOWN - Module shutdown */
PHP_MSHUTDOWN(random), /* PHP_MSHUTDOWN - Module shutdown */
PHP_RINIT(random), /* PHP_RINIT - Request initialization */
NULL, /* PHP_RSHUTDOWN - Request shutdown */
NULL, /* PHP_MINFO - Module info */
PHP_VERSION, /* Version */
PHP_MODULE_GLOBALS(random), /* ZTS Module globals */
PHP_GINIT(random), /* PHP_GINIT - Global initialization */
PHP_GSHUTDOWN(random), /* PHP_GSHUTDOWN - Global shutdown */
NULL, /* PHP_GSHUTDOWN - Global shutdown */
NULL, /* Post deactivate */
STANDARD_MODULE_PROPERTIES_EX
};
Expand Down
Loading