Skip to content

Commit 3874016

Browse files
committed
Add php_random_bytes_ex()
This variant can be used before RANDOM_G() is initialized
1 parent 1ab4520 commit 3874016

File tree

4 files changed

+75
-40
lines changed

4 files changed

+75
-40
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: 49 additions & 27 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,6 +210,18 @@ PHPAPI zend_result php_random_bytes(void *bytes, size_t size, bool should_throw)
210210
return SUCCESS;
211211
}
212212

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+
213225
PHPAPI zend_result php_random_int(zend_long min, zend_long max, zend_long *result, bool should_throw)
214226
{
215227
zend_ulong umax;
@@ -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_csprng.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020

2121
# include "php.h"
2222

23-
PHPAPI zend_result php_random_bytes(void *bytes, size_t size, 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);
25+
2426
PHPAPI zend_result php_random_int(zend_long min, zend_long max, zend_long *result, bool should_throw);
2527

2628
static inline zend_result php_random_bytes_throw(void *bytes, size_t size)
@@ -43,4 +45,6 @@ static inline zend_result php_random_int_silent(zend_long min, zend_long max, ze
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 & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -688,16 +688,6 @@ static PHP_GINIT_FUNCTION(random)
688688
}
689689
/* }}} */
690690

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-
701691
/* {{{ PHP_MINIT_FUNCTION */
702692
PHP_MINIT_FUNCTION(random)
703693
{
@@ -766,6 +756,15 @@ PHP_MINIT_FUNCTION(random)
766756
}
767757
/* }}} */
768758

759+
/* {{{ PHP_MSHUTDOWN_FUNCTION */
760+
PHP_MSHUTDOWN_FUNCTION(random)
761+
{
762+
php_random_csprng_shutdown();
763+
764+
return SUCCESS;
765+
}
766+
/* }}} */
767+
769768
/* {{{ PHP_RINIT_FUNCTION */
770769
PHP_RINIT_FUNCTION(random)
771770
{
@@ -782,14 +781,14 @@ zend_module_entry random_module_entry = {
782781
"random", /* Extension name */
783782
ext_functions, /* zend_function_entry */
784783
PHP_MINIT(random), /* PHP_MINIT - Module initialization */
785-
NULL, /* PHP_MSHUTDOWN - Module shutdown */
784+
PHP_MSHUTDOWN(random), /* PHP_MSHUTDOWN - Module shutdown */
786785
PHP_RINIT(random), /* PHP_RINIT - Request initialization */
787786
NULL, /* PHP_RSHUTDOWN - Request shutdown */
788787
NULL, /* PHP_MINFO - Module info */
789788
PHP_VERSION, /* Version */
790789
PHP_MODULE_GLOBALS(random), /* ZTS Module globals */
791790
PHP_GINIT(random), /* PHP_GINIT - Global initialization */
792-
PHP_GSHUTDOWN(random), /* PHP_GSHUTDOWN - Global shutdown */
791+
NULL, /* PHP_GSHUTDOWN - Global shutdown */
793792
NULL, /* Post deactivate */
794793
STANDARD_MODULE_PROPERTIES_EX
795794
};

0 commit comments

Comments
 (0)