Skip to content

Commit 44c199c

Browse files
authored
random: Make php_random_bytes() useable early during engine startup (#14291)
php_random_bytes() can now be used before RANDOM_G() is initialized
1 parent c7bdf30 commit 44c199c

File tree

5 files changed

+81
-48
lines changed

5 files changed

+81
-48
lines changed

Zend/zend_portability.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,16 @@ char *alloca();
269269
# define ZEND_ATTRIBUTE_UNUSED
270270
#endif
271271

272+
#if ZEND_GCC_VERSION >= 3003 || __has_attribute(nonnull)
273+
/* All pointer arguments must be non-null */
274+
# define ZEND_ATTRIBUTE_NONNULL __attribute__((nonnull))
275+
/* Specified arguments must be non-null (1-based) */
276+
# define ZEND_ATTRIBUTE_NONNULL_ARGS(...) __attribute__((nonnull(__VA_ARGS__)))
277+
#else
278+
# define ZEND_ATTRIBUTE_NONNULL
279+
# define ZEND_ATTRIBUTE_NONNULL_ARGS(...)
280+
#endif
281+
272282
#if defined(__GNUC__) && ZEND_GCC_VERSION >= 4003
273283
# define ZEND_COLD __attribute__((cold))
274284
# ifdef __OPTIMIZE__

ext/random/csprng.c

Lines changed: 50 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "php.h"
2727

2828
#include "Zend/zend_exceptions.h"
29+
#include "Zend/zend_atomic.h"
2930

3031
#include "php_random.h"
3132
#include "php_random_csprng.h"
@@ -61,14 +62,16 @@
6162
# include <sanitizer/msan_interface.h>
6263
#endif
6364

64-
PHPAPI zend_result php_random_bytes(void *bytes, size_t size, bool should_throw)
65+
#ifndef PHP_WIN32
66+
static zend_atomic_int random_fd = ZEND_ATOMIC_INT_INITIALIZER(-1);
67+
#endif
68+
69+
ZEND_ATTRIBUTE_NONNULL PHPAPI zend_result php_random_bytes_ex(void *bytes, size_t size, char *errstr, size_t errstr_size)
6570
{
6671
#ifdef PHP_WIN32
6772
/* Defer to CryptGenRandom on Windows */
6873
if (php_win32_get_random_bytes(bytes, size) == FAILURE) {
69-
if (should_throw) {
70-
zend_throw_exception(random_ce_Random_RandomException, "Failed to retrieve randomness from the operating system (BCryptGenRandom)", 0);
71-
}
74+
snprintf(errstr, errstr_size, "Failed to retrieve randomness from the operating system (BCryptGenRandom)");
7275
return FAILURE;
7376
}
7477
#elif HAVE_COMMONCRYPTO_COMMONRANDOM_H
@@ -79,9 +82,7 @@ PHPAPI zend_result php_random_bytes(void *bytes, size_t size, bool should_throw)
7982
* the vast majority of the time, it works fine ; but better make sure we catch failures
8083
*/
8184
if (CCRandomGenerateBytes(bytes, size) != kCCSuccess) {
82-
if (should_throw) {
83-
zend_throw_exception(random_ce_Random_RandomException, "Failed to retrieve randomness from the operating system (CCRandomGenerateBytes)", 0);
84-
}
85+
snprintf(errstr, errstr_size, "Failed to retrieve randomness from the operating system (CCRandomGenerateBytes)");
8586
return FAILURE;
8687
}
8788
#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)
147148
}
148149
# endif
149150
if (read_bytes < size) {
150-
int fd = RANDOM_G(random_fd);
151+
int fd = zend_atomic_int_load_ex(&random_fd);
151152
struct stat st;
152153

153154
if (fd < 0) {
154155
errno = 0;
155156
fd = open("/dev/urandom", O_RDONLY);
156157
if (fd < 0) {
157-
if (should_throw) {
158-
if (errno != 0) {
159-
zend_throw_exception_ex(random_ce_Random_RandomException, 0, "Cannot open /dev/urandom: %s", strerror(errno));
160-
} else {
161-
zend_throw_exception_ex(random_ce_Random_RandomException, 0, "Cannot open /dev/urandom");
162-
}
158+
if (errno != 0) {
159+
snprintf(errstr, errstr_size, "Cannot open /dev/urandom: %s", strerror(errno));
160+
} else {
161+
snprintf(errstr, errstr_size, "Cannot open /dev/urandom");
163162
}
164163
return FAILURE;
165164
}
@@ -174,16 +173,19 @@ PHPAPI zend_result php_random_bytes(void *bytes, size_t size, bool should_throw)
174173
# endif
175174
) {
176175
close(fd);
177-
if (should_throw) {
178-
if (errno != 0) {
179-
zend_throw_exception_ex(random_ce_Random_RandomException, 0, "Error reading from /dev/urandom: %s", strerror(errno));
180-
} else {
181-
zend_throw_exception_ex(random_ce_Random_RandomException, 0, "Error reading from /dev/urandom");
182-
}
176+
if (errno != 0) {
177+
snprintf(errstr, errstr_size, "Error reading from /dev/urandom: %s", strerror(errno));
178+
} else {
179+
snprintf(errstr, errstr_size, "Error reading from /dev/urandom");
183180
}
184181
return FAILURE;
185182
}
186-
RANDOM_G(random_fd) = fd;
183+
int expected = -1;
184+
if (!zend_atomic_int_compare_exchange_ex(&random_fd, &expected, fd)) {
185+
close(fd);
186+
/* expected is now the actual value of random_fd */
187+
fd = expected;
188+
}
187189
}
188190

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

194196
if (n <= 0) {
195-
if (should_throw) {
196-
if (errno != 0) {
197-
zend_throw_exception_ex(random_ce_Random_RandomException, 0, "Could not gather sufficient random data: %s", strerror(errno));
198-
} else {
199-
zend_throw_exception_ex(random_ce_Random_RandomException, 0, "Could not gather sufficient random data");
200-
}
197+
if (errno != 0) {
198+
snprintf(errstr, errstr_size, "Could not gather sufficient random data: %s", strerror(errno));
199+
} else {
200+
snprintf(errstr, errstr_size, "Could not gather sufficient random data");
201201
}
202202
return FAILURE;
203203
}
@@ -210,7 +210,19 @@ PHPAPI zend_result php_random_bytes(void *bytes, size_t size, bool should_throw)
210210
return SUCCESS;
211211
}
212212

213-
PHPAPI zend_result php_random_int(zend_long min, zend_long max, zend_long *result, bool should_throw)
213+
ZEND_ATTRIBUTE_NONNULL PHPAPI zend_result php_random_bytes(void *bytes, size_t size, bool should_throw)
214+
{
215+
char errstr[128];
216+
zend_result result = php_random_bytes_ex(bytes, size, errstr, sizeof(errstr));
217+
218+
if (result == FAILURE && should_throw) {
219+
zend_throw_exception(random_ce_Random_RandomException, errstr, 0);
220+
}
221+
222+
return result;
223+
}
224+
225+
ZEND_ATTRIBUTE_NONNULL PHPAPI zend_result php_random_int(zend_long min, zend_long max, zend_long *result, bool should_throw)
214226
{
215227
zend_ulong umax;
216228
zend_ulong trial;
@@ -251,3 +263,13 @@ PHPAPI zend_result php_random_int(zend_long min, zend_long max, zend_long *resul
251263
*result = (zend_long)((trial % umax) + min);
252264
return SUCCESS;
253265
}
266+
267+
PHPAPI void php_random_csprng_shutdown(void)
268+
{
269+
#ifndef PHP_WIN32
270+
int fd = zend_atomic_int_exchange(&random_fd, -1);
271+
if (fd != -1) {
272+
close(fd);
273+
}
274+
#endif
275+
}

ext/random/php_random.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,6 @@ PHP_MSHUTDOWN_FUNCTION(random);
195195
PHP_RINIT_FUNCTION(random);
196196

197197
ZEND_BEGIN_MODULE_GLOBALS(random)
198-
int random_fd;
199198
bool combined_lcg_seeded;
200199
bool mt19937_seeded;
201200
bool fallback_seed_initialized;

ext/random/php_random_csprng.h

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,27 +20,31 @@
2020

2121
# include "php.h"
2222

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

26-
static inline zend_result php_random_bytes_throw(void *bytes, size_t size)
26+
ZEND_ATTRIBUTE_NONNULL PHPAPI zend_result php_random_int(zend_long min, zend_long max, zend_long *result, bool should_throw);
27+
28+
ZEND_ATTRIBUTE_NONNULL static inline zend_result php_random_bytes_throw(void *bytes, size_t size)
2729
{
2830
return php_random_bytes(bytes, size, true);
2931
}
3032

31-
static inline zend_result php_random_bytes_silent(void *bytes, size_t size)
33+
ZEND_ATTRIBUTE_NONNULL static inline zend_result php_random_bytes_silent(void *bytes, size_t size)
3234
{
3335
return php_random_bytes(bytes, size, false);
3436
}
3537

36-
static inline zend_result php_random_int_throw(zend_long min, zend_long max, zend_long *result)
38+
ZEND_ATTRIBUTE_NONNULL static inline zend_result php_random_int_throw(zend_long min, zend_long max, zend_long *result)
3739
{
3840
return php_random_int(min, max, result, true);
3941
}
4042

41-
static inline zend_result php_random_int_silent(zend_long min, zend_long max, zend_long *result)
43+
ZEND_ATTRIBUTE_NONNULL static inline zend_result php_random_int_silent(zend_long min, zend_long max, zend_long *result)
4244
{
4345
return php_random_int(min, max, result, false);
4446
}
4547

48+
PHPAPI void php_random_csprng_shutdown(void);
49+
4650
#endif /* PHP_RANDOM_CSPRNG_H */

ext/random/random.c

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -683,21 +683,10 @@ PHPAPI uint64_t php_random_generate_fallback_seed(void)
683683
/* {{{ PHP_GINIT_FUNCTION */
684684
static PHP_GINIT_FUNCTION(random)
685685
{
686-
random_globals->random_fd = -1;
687686
random_globals->fallback_seed_initialized = false;
688687
}
689688
/* }}} */
690689

691-
/* {{{ PHP_GSHUTDOWN_FUNCTION */
692-
static PHP_GSHUTDOWN_FUNCTION(random)
693-
{
694-
if (random_globals->random_fd >= 0) {
695-
close(random_globals->random_fd);
696-
random_globals->random_fd = -1;
697-
}
698-
}
699-
/* }}} */
700-
701690
/* {{{ PHP_MINIT_FUNCTION */
702691
PHP_MINIT_FUNCTION(random)
703692
{
@@ -766,6 +755,15 @@ PHP_MINIT_FUNCTION(random)
766755
}
767756
/* }}} */
768757

758+
/* {{{ PHP_MSHUTDOWN_FUNCTION */
759+
PHP_MSHUTDOWN_FUNCTION(random)
760+
{
761+
php_random_csprng_shutdown();
762+
763+
return SUCCESS;
764+
}
765+
/* }}} */
766+
769767
/* {{{ PHP_RINIT_FUNCTION */
770768
PHP_RINIT_FUNCTION(random)
771769
{
@@ -782,14 +780,14 @@ zend_module_entry random_module_entry = {
782780
"random", /* Extension name */
783781
ext_functions, /* zend_function_entry */
784782
PHP_MINIT(random), /* PHP_MINIT - Module initialization */
785-
NULL, /* PHP_MSHUTDOWN - Module shutdown */
783+
PHP_MSHUTDOWN(random), /* PHP_MSHUTDOWN - Module shutdown */
786784
PHP_RINIT(random), /* PHP_RINIT - Request initialization */
787785
NULL, /* PHP_RSHUTDOWN - Request shutdown */
788786
NULL, /* PHP_MINFO - Module info */
789787
PHP_VERSION, /* Version */
790788
PHP_MODULE_GLOBALS(random), /* ZTS Module globals */
791789
PHP_GINIT(random), /* PHP_GINIT - Global initialization */
792-
PHP_GSHUTDOWN(random), /* PHP_GSHUTDOWN - Global shutdown */
790+
NULL, /* PHP_GSHUTDOWN - Global shutdown */
793791
NULL, /* Post deactivate */
794792
STANDARD_MODULE_PROPERTIES_EX
795793
};

0 commit comments

Comments
 (0)