26
26
#include "php.h"
27
27
28
28
#include "Zend/zend_exceptions.h"
29
+ #include "Zend/zend_atomic.h"
29
30
30
31
#include "php_random.h"
31
32
#include "php_random_csprng.h"
61
62
# include <sanitizer/msan_interface.h>
62
63
#endif
63
64
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 )
65
70
{
66
71
#ifdef PHP_WIN32
67
72
/* Defer to CryptGenRandom on Windows */
68
73
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)" );
72
75
return FAILURE ;
73
76
}
74
77
#elif HAVE_COMMONCRYPTO_COMMONRANDOM_H
@@ -79,9 +82,7 @@ PHPAPI zend_result php_random_bytes(void *bytes, size_t size, bool should_throw)
79
82
* the vast majority of the time, it works fine ; but better make sure we catch failures
80
83
*/
81
84
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)" );
85
86
return FAILURE ;
86
87
}
87
88
#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)
147
148
}
148
149
# endif
149
150
if (read_bytes < size ) {
150
- int fd = RANDOM_G ( random_fd );
151
+ int fd = zend_atomic_int_load_ex ( & random_fd );
151
152
struct stat st ;
152
153
153
154
if (fd < 0 ) {
154
155
errno = 0 ;
155
156
fd = open ("/dev/urandom" , O_RDONLY );
156
157
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" );
163
162
}
164
163
return FAILURE ;
165
164
}
@@ -174,16 +173,19 @@ PHPAPI zend_result php_random_bytes(void *bytes, size_t size, bool should_throw)
174
173
# endif
175
174
) {
176
175
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" );
183
180
}
184
181
return FAILURE ;
185
182
}
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
+ }
187
189
}
188
190
189
191
read_bytes = 0 ;
@@ -192,12 +194,10 @@ PHPAPI zend_result php_random_bytes(void *bytes, size_t size, bool should_throw)
192
194
ssize_t n = read (fd , bytes + read_bytes , size - read_bytes );
193
195
194
196
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" );
201
201
}
202
202
return FAILURE ;
203
203
}
@@ -210,7 +210,19 @@ PHPAPI zend_result php_random_bytes(void *bytes, size_t size, bool should_throw)
210
210
return SUCCESS ;
211
211
}
212
212
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 )
214
226
{
215
227
zend_ulong umax ;
216
228
zend_ulong trial ;
@@ -251,3 +263,13 @@ PHPAPI zend_result php_random_int(zend_long min, zend_long max, zend_long *resul
251
263
* result = (zend_long )((trial % umax ) + min );
252
264
return SUCCESS ;
253
265
}
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
+ }
0 commit comments