diff --git a/Zend/zend_portability.h b/Zend/zend_portability.h index af7dcf68ccd7b..99eb5ab135359 100644 --- a/Zend/zend_portability.h +++ b/Zend/zend_portability.h @@ -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__ diff --git a/ext/random/csprng.c b/ext/random/csprng.c index 1bb31bffa20ff..e349f1046f6ea 100644 --- a/ext/random/csprng.c +++ b/ext/random/csprng.c @@ -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" @@ -61,14 +62,16 @@ # include #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 @@ -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) || \ @@ -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; } @@ -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; @@ -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; } @@ -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; @@ -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 +} diff --git a/ext/random/php_random.h b/ext/random/php_random.h index 4dac912e75a7d..9ec31993b6adb 100644 --- a/ext/random/php_random.h +++ b/ext/random/php_random.h @@ -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; diff --git a/ext/random/php_random_csprng.h b/ext/random/php_random_csprng.h index 64a7050d50bd7..7fb0d7357f614 100644 --- a/ext/random/php_random_csprng.h +++ b/ext/random/php_random_csprng.h @@ -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 */ diff --git a/ext/random/random.c b/ext/random/random.c index 0474d17e756b7..7af7603238706 100644 --- a/ext/random/random.c +++ b/ext/random/random.c @@ -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) { @@ -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) { @@ -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 };