Skip to content

Commit

Permalink
Add php_random_bytes_ex()
Browse files Browse the repository at this point in the history
This variant can be used before RANDOM_G() is initialized
  • Loading branch information
arnaud-lb committed May 21, 2024
1 parent 4d957e2 commit d03040d
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 40 deletions.
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
76 changes: 49 additions & 27 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,6 +210,18 @@ PHPAPI zend_result php_random_bytes(void *bytes, size_t size, bool should_throw)
return SUCCESS;
}

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;
}

PHPAPI zend_result php_random_int(zend_long min, zend_long max, zend_long *result, bool should_throw)
{
zend_ulong umax;
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
}
6 changes: 5 additions & 1 deletion ext/random/php_random_csprng.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@

# include "php.h"

PHPAPI zend_result php_random_bytes(void *bytes, size_t size, 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);

PHPAPI zend_result php_random_int(zend_long min, zend_long max, zend_long *result, bool should_throw);

static inline zend_result php_random_bytes_throw(void *bytes, size_t size)
Expand All @@ -43,4 +45,6 @@ static inline zend_result php_random_int_silent(zend_long min, zend_long max, ze
return php_random_int(min, max, result, false);
}

PHPAPI void php_random_csprng_shutdown(void);

#endif /* PHP_RANDOM_CSPRNG_H */
23 changes: 11 additions & 12 deletions ext/random/random.c
Original file line number Diff line number Diff line change
Expand Up @@ -702,16 +702,6 @@ static PHP_GINIT_FUNCTION(random)
}
/* }}} */

/* {{{ 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 @@ -780,6 +770,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 @@ -796,14 +795,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

0 comments on commit d03040d

Please sign in to comment.