xref: /php-src/ext/random/random.c (revision 6c8ef1d9)
1 /*
2    +----------------------------------------------------------------------+
3    | Copyright (c) The PHP Group                                          |
4    +----------------------------------------------------------------------+
5    | This source file is subject to version 3.01 of the PHP license,      |
6    | that is bundled with this package in the file LICENSE, and is        |
7    | available through the world-wide-web at the following url:           |
8    | https://www.php.net/license/3_01.txt                                 |
9    | If you did not receive a copy of the PHP license and are unable to   |
10    | obtain it through the world-wide-web, please send a note to          |
11    | license@php.net so we can mail you a copy immediately.               |
12    +----------------------------------------------------------------------+
13    | Authors: Sammy Kaye Powers <me@sammyk.me>                            |
14    |          Go Kudo <zeriyoshi@php.net>                                 |
15    +----------------------------------------------------------------------+
16 */
17 
18 #ifdef HAVE_CONFIG_H
19 # include "config.h"
20 #endif
21 
22 #include <stdlib.h>
23 #include <sys/stat.h>
24 #include <fcntl.h>
25 #include <math.h>
26 
27 #include "php.h"
28 
29 #include "Zend/zend_enum.h"
30 #include "Zend/zend_exceptions.h"
31 
32 #include "php_random.h"
33 
34 #if HAVE_UNISTD_H
35 # include <unistd.h>
36 #endif
37 
38 #ifdef PHP_WIN32
39 # include "win32/time.h"
40 # include "win32/winutil.h"
41 # include <process.h>
42 #else
43 # include <sys/time.h>
44 #endif
45 
46 #ifdef __linux__
47 # include <sys/syscall.h>
48 #endif
49 
50 #if HAVE_SYS_PARAM_H
51 # include <sys/param.h>
52 # if (__FreeBSD__ && __FreeBSD_version > 1200000) || (__DragonFly__ && __DragonFly_version >= 500700) || \
53      defined(__sun) || (defined(__NetBSD__) && __NetBSD_Version__ >= 1000000000)
54 #  include <sys/random.h>
55 # endif
56 #endif
57 
58 #if HAVE_COMMONCRYPTO_COMMONRANDOM_H
59 # include <CommonCrypto/CommonCryptoError.h>
60 # include <CommonCrypto/CommonRandom.h>
61 #endif
62 
63 #if __has_feature(memory_sanitizer)
64 # include <sanitizer/msan_interface.h>
65 #endif
66 
67 #include "random_arginfo.h"
68 
69 PHPAPI ZEND_DECLARE_MODULE_GLOBALS(random)
70 
71 PHPAPI zend_class_entry *random_ce_Random_Engine;
72 PHPAPI zend_class_entry *random_ce_Random_CryptoSafeEngine;
73 
74 PHPAPI zend_class_entry *random_ce_Random_Engine_Mt19937;
75 PHPAPI zend_class_entry *random_ce_Random_Engine_PcgOneseq128XslRr64;
76 PHPAPI zend_class_entry *random_ce_Random_Engine_Xoshiro256StarStar;
77 PHPAPI zend_class_entry *random_ce_Random_Engine_Secure;
78 
79 PHPAPI zend_class_entry *random_ce_Random_Randomizer;
80 
81 PHPAPI zend_class_entry *random_ce_Random_IntervalBoundary;
82 
83 PHPAPI zend_class_entry *random_ce_Random_RandomError;
84 PHPAPI zend_class_entry *random_ce_Random_BrokenRandomEngineError;
85 PHPAPI zend_class_entry *random_ce_Random_RandomException;
86 
87 static zend_object_handlers random_engine_mt19937_object_handlers;
88 static zend_object_handlers random_engine_pcgoneseq128xslrr64_object_handlers;
89 static zend_object_handlers random_engine_xoshiro256starstar_object_handlers;
90 static zend_object_handlers random_engine_secure_object_handlers;
91 static zend_object_handlers random_randomizer_object_handlers;
92 
php_random_range32(const php_random_algo * algo,php_random_status * status,uint32_t umax)93 PHPAPI uint32_t php_random_range32(const php_random_algo *algo, php_random_status *status, uint32_t umax)
94 {
95 	uint32_t result, limit;
96 	size_t total_size = 0;
97 	uint32_t count = 0;
98 
99 	result = 0;
100 	total_size = 0;
101 	do {
102 		uint32_t r = algo->generate(status);
103 		result = result | (r << (total_size * 8));
104 		total_size += status->last_generated_size;
105 		if (EG(exception)) {
106 			return 0;
107 		}
108 	} while (total_size < sizeof(uint32_t));
109 
110 	/* Special case where no modulus is required */
111 	if (UNEXPECTED(umax == UINT32_MAX)) {
112 		return result;
113 	}
114 
115 	/* Increment the max so range is inclusive of max */
116 	umax++;
117 
118 	/* Powers of two are not biased */
119 	if ((umax & (umax - 1)) == 0) {
120 		return result & (umax - 1);
121 	}
122 
123 	/* Ceiling under which UINT32_MAX % max == 0 */
124 	limit = UINT32_MAX - (UINT32_MAX % umax) - 1;
125 
126 	/* Discard numbers over the limit to avoid modulo bias */
127 	while (UNEXPECTED(result > limit)) {
128 		/* If the requirements cannot be met in a cycles, return fail */
129 		if (++count > PHP_RANDOM_RANGE_ATTEMPTS) {
130 			zend_throw_error(random_ce_Random_BrokenRandomEngineError, "Failed to generate an acceptable random number in %d attempts", PHP_RANDOM_RANGE_ATTEMPTS);
131 			return 0;
132 		}
133 
134 		result = 0;
135 		total_size = 0;
136 		do {
137 			uint32_t r = algo->generate(status);
138 			result = result | (r << (total_size * 8));
139 			total_size += status->last_generated_size;
140 			if (EG(exception)) {
141 				return 0;
142 			}
143 		} while (total_size < sizeof(uint32_t));
144 	}
145 
146 	return result % umax;
147 }
148 
php_random_range64(const php_random_algo * algo,php_random_status * status,uint64_t umax)149 PHPAPI uint64_t php_random_range64(const php_random_algo *algo, php_random_status *status, uint64_t umax)
150 {
151 	uint64_t result, limit;
152 	size_t total_size = 0;
153 	uint32_t count = 0;
154 
155 	result = 0;
156 	total_size = 0;
157 	do {
158 		uint64_t r = algo->generate(status);
159 		result = result | (r << (total_size * 8));
160 		total_size += status->last_generated_size;
161 		if (EG(exception)) {
162 			return 0;
163 		}
164 	} while (total_size < sizeof(uint64_t));
165 
166 	/* Special case where no modulus is required */
167 	if (UNEXPECTED(umax == UINT64_MAX)) {
168 		return result;
169 	}
170 
171 	/* Increment the max so range is inclusive of max */
172 	umax++;
173 
174 	/* Powers of two are not biased */
175 	if ((umax & (umax - 1)) == 0) {
176 		return result & (umax - 1);
177 	}
178 
179 	/* Ceiling under which UINT64_MAX % max == 0 */
180 	limit = UINT64_MAX - (UINT64_MAX % umax) - 1;
181 
182 	/* Discard numbers over the limit to avoid modulo bias */
183 	while (UNEXPECTED(result > limit)) {
184 		/* If the requirements cannot be met in a cycles, return fail */
185 		if (++count > PHP_RANDOM_RANGE_ATTEMPTS) {
186 			zend_throw_error(random_ce_Random_BrokenRandomEngineError, "Failed to generate an acceptable random number in %d attempts", PHP_RANDOM_RANGE_ATTEMPTS);
187 			return 0;
188 		}
189 
190 		result = 0;
191 		total_size = 0;
192 		do {
193 			uint64_t r = algo->generate(status);
194 			result = result | (r << (total_size * 8));
195 			total_size += status->last_generated_size;
196 			if (EG(exception)) {
197 				return 0;
198 			}
199 		} while (total_size < sizeof(uint64_t));
200 	}
201 
202 	return result % umax;
203 }
204 
php_random_engine_mt19937_new(zend_class_entry * ce)205 static zend_object *php_random_engine_mt19937_new(zend_class_entry *ce)
206 {
207 	return &php_random_engine_common_init(ce, &random_engine_mt19937_object_handlers, &php_random_algo_mt19937)->std;
208 }
209 
php_random_engine_pcgoneseq128xslrr64_new(zend_class_entry * ce)210 static zend_object *php_random_engine_pcgoneseq128xslrr64_new(zend_class_entry *ce)
211 {
212 	return &php_random_engine_common_init(ce, &random_engine_pcgoneseq128xslrr64_object_handlers, &php_random_algo_pcgoneseq128xslrr64)->std;
213 }
214 
php_random_engine_xoshiro256starstar_new(zend_class_entry * ce)215 static zend_object *php_random_engine_xoshiro256starstar_new(zend_class_entry *ce)
216 {
217 	return &php_random_engine_common_init(ce, &random_engine_xoshiro256starstar_object_handlers, &php_random_algo_xoshiro256starstar)->std;
218 }
219 
php_random_engine_secure_new(zend_class_entry * ce)220 static zend_object *php_random_engine_secure_new(zend_class_entry *ce)
221 {
222 	return &php_random_engine_common_init(ce, &random_engine_secure_object_handlers, &php_random_algo_secure)->std;
223 }
224 
php_random_randomizer_new(zend_class_entry * ce)225 static zend_object *php_random_randomizer_new(zend_class_entry *ce)
226 {
227 	php_random_randomizer *randomizer = zend_object_alloc(sizeof(php_random_randomizer), ce);
228 
229 	zend_object_std_init(&randomizer->std, ce);
230 	object_properties_init(&randomizer->std, ce);
231 
232 	return &randomizer->std;
233 }
234 
randomizer_free_obj(zend_object * object)235 static void randomizer_free_obj(zend_object *object) {
236 	php_random_randomizer *randomizer = php_random_randomizer_from_obj(object);
237 
238 	if (randomizer->is_userland_algo) {
239 		php_random_status_free(randomizer->status, false);
240 	}
241 
242 	zend_object_std_dtor(&randomizer->std);
243 }
244 
php_random_status_alloc(const php_random_algo * algo,const bool persistent)245 PHPAPI php_random_status *php_random_status_alloc(const php_random_algo *algo, const bool persistent)
246 {
247 	php_random_status *status = pecalloc(1, sizeof(php_random_status), persistent);
248 
249 	status->last_generated_size = algo->generate_size;
250 	status->state = algo->state_size > 0 ? pecalloc(1, algo->state_size, persistent) : NULL;
251 
252 	return status;
253 }
254 
php_random_status_copy(const php_random_algo * algo,php_random_status * old_status,php_random_status * new_status)255 PHPAPI php_random_status *php_random_status_copy(const php_random_algo *algo, php_random_status *old_status, php_random_status *new_status)
256 {
257 	new_status->last_generated_size = old_status->last_generated_size;
258 	new_status->state = memcpy(new_status->state, old_status->state, algo->state_size);
259 
260 	return new_status;
261 }
262 
php_random_status_free(php_random_status * status,const bool persistent)263 PHPAPI void php_random_status_free(php_random_status *status, const bool persistent)
264 {
265 	if (status != NULL) {
266 		pefree(status->state, persistent);
267 	}
268 
269 	pefree(status, persistent);
270 }
271 
php_random_engine_common_init(zend_class_entry * ce,zend_object_handlers * handlers,const php_random_algo * algo)272 PHPAPI php_random_engine *php_random_engine_common_init(zend_class_entry *ce, zend_object_handlers *handlers, const php_random_algo *algo)
273 {
274 	php_random_engine *engine = zend_object_alloc(sizeof(php_random_engine), ce);
275 
276 	zend_object_std_init(&engine->std, ce);
277 	object_properties_init(&engine->std, ce);
278 
279 	engine->algo = algo;
280 	engine->status = php_random_status_alloc(engine->algo, false);
281 	engine->std.handlers = handlers;
282 
283 	return engine;
284 }
285 
php_random_engine_common_free_object(zend_object * object)286 PHPAPI void php_random_engine_common_free_object(zend_object *object)
287 {
288 	php_random_engine *engine = php_random_engine_from_obj(object);
289 
290 	php_random_status_free(engine->status, false);
291 	zend_object_std_dtor(object);
292 }
293 
php_random_engine_common_clone_object(zend_object * object)294 PHPAPI zend_object *php_random_engine_common_clone_object(zend_object *object)
295 {
296 	php_random_engine *old_engine = php_random_engine_from_obj(object);
297 	php_random_engine *new_engine = php_random_engine_from_obj(old_engine->std.ce->create_object(old_engine->std.ce));
298 
299 	new_engine->algo = old_engine->algo;
300 	if (old_engine->status) {
301 		new_engine->status = php_random_status_copy(old_engine->algo, old_engine->status, new_engine->status);
302 	}
303 
304 	zend_objects_clone_members(&new_engine->std, &old_engine->std);
305 
306 	return &new_engine->std;
307 }
308 
309 /* {{{ php_random_range */
php_random_range(const php_random_algo * algo,php_random_status * status,zend_long min,zend_long max)310 PHPAPI zend_long php_random_range(const php_random_algo *algo, php_random_status *status, zend_long min, zend_long max)
311 {
312 	zend_ulong umax = (zend_ulong) max - (zend_ulong) min;
313 
314 	if (umax > UINT32_MAX) {
315 		return (zend_long) (php_random_range64(algo, status, umax) + min);
316 	}
317 
318 	return (zend_long) (php_random_range32(algo, status, umax) + min);
319 }
320 /* }}} */
321 
322 /* {{{ php_random_default_algo */
php_random_default_algo(void)323 PHPAPI const php_random_algo *php_random_default_algo(void)
324 {
325 	return &php_random_algo_mt19937;
326 }
327 /* }}} */
328 
329 /* {{{ php_random_default_status */
php_random_default_status(void)330 PHPAPI php_random_status *php_random_default_status(void)
331 {
332 	php_random_status *status = RANDOM_G(mt19937);
333 
334 	if (!RANDOM_G(mt19937_seeded)) {
335 		php_random_mt19937_seed_default(status->state);
336 		RANDOM_G(mt19937_seeded) = true;
337 	}
338 
339 	return status;
340 }
341 /* }}} */
342 
343 /* this is read-only, so it's ok */
344 ZEND_SET_ALIGNED(16, static const char hexconvtab[]) = "0123456789abcdef";
345 
346 /* {{{ php_random_bin2hex_le */
347 /* stolen from standard/string.c */
php_random_bin2hex_le(const void * ptr,const size_t len)348 PHPAPI zend_string *php_random_bin2hex_le(const void *ptr, const size_t len)
349 {
350 	zend_string *str;
351 	size_t i;
352 
353 	str = zend_string_safe_alloc(len, 2 * sizeof(char), 0, 0);
354 
355 	i = 0;
356 #ifdef WORDS_BIGENDIAN
357 	/* force little endian */
358 	for (zend_long j = (len - 1); 0 <= j; j--) {
359 #else
360 	for (zend_long j = 0; j < len; j++) {
361 #endif
362 		ZSTR_VAL(str)[i++] = hexconvtab[((unsigned char *) ptr)[j] >> 4];
363 		ZSTR_VAL(str)[i++] = hexconvtab[((unsigned char *) ptr)[j] & 15];
364 	}
365 	ZSTR_VAL(str)[i] = '\0';
366 
367 	return str;
368 }
369 /* }}} */
370 
371 /* {{{ php_random_hex2bin_le */
372 /* stolen from standard/string.c */
373 PHPAPI bool php_random_hex2bin_le(zend_string *hexstr, void *dest)
374 {
375 	size_t len = hexstr->len >> 1;
376 	unsigned char *str = (unsigned char *) hexstr->val, c, l, d;
377 	unsigned char *ptr = (unsigned char *) dest;
378 	int is_letter, i = 0;
379 
380 #ifdef WORDS_BIGENDIAN
381 	/* force little endian */
382 	for (zend_long j = (len - 1); 0 <= j; j--) {
383 #else
384 	for (zend_long j = 0; j < len; j++) {
385 #endif
386 		c = str[i++];
387 		l = c & ~0x20;
388 		is_letter = ((uint32_t) ((l - 'A') ^ (l - 'F' - 1))) >> (8 * sizeof(uint32_t) - 1);
389 
390 		/* basically (c >= '0' && c <= '9') || (l >= 'A' && l <= 'F') */
391 		if (EXPECTED((((c ^ '0') - 10) >> (8 * sizeof(uint32_t) - 1)) | is_letter)) {
392 			d = (l - 0x10 - 0x27 * is_letter) << 4;
393 		} else {
394 			return false;
395 		}
396 		c = str[i++];
397 		l = c & ~0x20;
398 		is_letter = ((uint32_t) ((l - 'A') ^ (l - 'F' - 1))) >> (8 * sizeof(uint32_t) - 1);
399 		if (EXPECTED((((c ^ '0') - 10) >> (8 * sizeof(uint32_t) - 1)) | is_letter)) {
400 			d |= l - 0x10 - 0x27 * is_letter;
401 		} else {
402 			return false;
403 		}
404 		ptr[j] = d;
405 	}
406 	return true;
407 }
408 /* }}} */
409 
410 /* {{{ php_combined_lcg */
411 PHPAPI double php_combined_lcg(void)
412 {
413 	php_random_status *status = RANDOM_G(combined_lcg);
414 
415 	if (!RANDOM_G(combined_lcg_seeded)) {
416 		php_random_combinedlcg_seed_default(status->state);
417 		RANDOM_G(combined_lcg_seeded) = true;
418 	}
419 
420 	return php_random_algo_combinedlcg.generate(status) * 4.656613e-10;
421 }
422 /* }}} */
423 
424 /* {{{ php_mt_srand */
425 PHPAPI void php_mt_srand(uint32_t seed)
426 {
427 	/* Seed the generator with a simple uint32 */
428 	php_random_algo_mt19937.seed(php_random_default_status(), (zend_long) seed);
429 }
430 /* }}} */
431 
432 /* {{{ php_mt_rand */
433 PHPAPI uint32_t php_mt_rand(void)
434 {
435 	return (uint32_t) php_random_algo_mt19937.generate(php_random_default_status());
436 }
437 /* }}} */
438 
439 /* {{{ php_mt_rand_range */
440 PHPAPI zend_long php_mt_rand_range(zend_long min, zend_long max)
441 {
442 	return php_random_algo_mt19937.range(php_random_default_status(), min, max);
443 }
444 /* }}} */
445 
446 /* {{{ php_mt_rand_common
447  * rand() allows min > max, mt_rand does not */
448 PHPAPI zend_long php_mt_rand_common(zend_long min, zend_long max)
449 {
450 	php_random_status *status = php_random_default_status();
451 	php_random_status_state_mt19937 *s = status->state;
452 
453 	if (s->mode == MT_RAND_MT19937) {
454 		return php_mt_rand_range(min, max);
455 	}
456 
457 	uint64_t r = php_random_algo_mt19937.generate(php_random_default_status()) >> 1;
458 
459 	/* This is an inlined version of the RAND_RANGE_BADSCALING macro that does not invoke UB when encountering
460 	 * (max - min) > ZEND_LONG_MAX.
461 	 */
462 	zend_ulong offset = (double) ( (double) max - min + 1.0) * (r / (PHP_MT_RAND_MAX + 1.0));
463 
464 	return (zend_long) (offset + min);
465 }
466 /* }}} */
467 
468 /* {{{ php_srand */
469 PHPAPI void php_srand(zend_long seed)
470 {
471 	php_mt_srand((uint32_t) seed);
472 }
473 /* }}} */
474 
475 /* {{{ php_rand */
476 PHPAPI zend_long php_rand(void)
477 {
478 	return php_mt_rand();
479 }
480 /* }}} */
481 
482 /* {{{ php_random_bytes */
483 PHPAPI int php_random_bytes(void *bytes, size_t size, bool should_throw)
484 {
485 #ifdef PHP_WIN32
486 	/* Defer to CryptGenRandom on Windows */
487 	if (php_win32_get_random_bytes(bytes, size) == FAILURE) {
488 		if (should_throw) {
489 			zend_throw_exception(random_ce_Random_RandomException, "Failed to retrieve randomness from the operating system (BCryptGenRandom)", 0);
490 		}
491 		return FAILURE;
492 	}
493 #elif HAVE_COMMONCRYPTO_COMMONRANDOM_H
494 	/*
495 	 * Purposely prioritized upon arc4random_buf for modern macOs releases
496 	 * arc4random api on this platform uses `ccrng_generate` which returns
497 	 * a status but silented to respect the "no fail" arc4random api interface
498 	 * the vast majority of the time, it works fine ; but better make sure we catch failures
499 	 */
500 	if (CCRandomGenerateBytes(bytes, size) != kCCSuccess) {
501 		if (should_throw) {
502 			zend_throw_exception(random_ce_Random_RandomException, "Failed to retrieve randomness from the operating system (CCRandomGenerateBytes)", 0);
503 		}
504 		return FAILURE;
505 	}
506 #elif HAVE_DECL_ARC4RANDOM_BUF && ((defined(__OpenBSD__) && OpenBSD >= 201405) || (defined(__NetBSD__) && __NetBSD_Version__ >= 700000001 && __NetBSD_Version__ < 1000000000) || \
507   defined(__APPLE__))
508 	/*
509 	 * OpenBSD until there is a valid equivalent
510 	 * or NetBSD before the 10.x release
511 	 * falls back to arc4random_buf
512 	 * giving a decent output, the main benefit
513 	 * is being (relatively) failsafe.
514 	 * Older macOs releases fall also into this
515 	 * category for reasons explained above.
516 	 */
517 	arc4random_buf(bytes, size);
518 #else
519 	size_t read_bytes = 0;
520 # if (defined(__linux__) && defined(SYS_getrandom)) || (defined(__FreeBSD__) && __FreeBSD_version >= 1200000) || (defined(__DragonFly__) && __DragonFly_version >= 500700) || \
521   defined(__sun) || (defined(__NetBSD__) && __NetBSD_Version__ >= 1000000000)
522 	/* Linux getrandom(2) syscall or FreeBSD/DragonFlyBSD/NetBSD getrandom(2) function
523 	 * Being a syscall, implemented in the kernel, getrandom offers higher quality output
524 	 * compared to the arc4random api albeit a fallback to /dev/urandom is considered.
525 	 */
526 	while (read_bytes < size) {
527 		/* Below, (bytes + read_bytes)  is pointer arithmetic.
528 
529 		   bytes   read_bytes  size
530 		     |      |           |
531 		    [#######=============] (we're going to write over the = region)
532 		             \\\\\\\\\\\\\
533 		              amount_to_read
534 		*/
535 		size_t amount_to_read = size - read_bytes;
536 		ssize_t n;
537 
538 		errno = 0;
539 #  if defined(__linux__)
540 		n = syscall(SYS_getrandom, bytes + read_bytes, amount_to_read, 0);
541 #  else
542 		n = getrandom(bytes + read_bytes, amount_to_read, 0);
543 #  endif
544 
545 		if (n == -1) {
546 			if (errno == ENOSYS) {
547 				/* This can happen if PHP was compiled against a newer kernel where getrandom()
548 				 * is available, but then runs on an older kernel without getrandom(). If this
549 				 * happens we simply fall back to reading from /dev/urandom. */
550 				ZEND_ASSERT(read_bytes == 0);
551 				break;
552 			} else if (errno == EINTR || errno == EAGAIN) {
553 				/* Try again */
554 				continue;
555 			} else {
556 				/* If the syscall fails, fall back to reading from /dev/urandom */
557 				break;
558 			}
559 		}
560 
561 #  if __has_feature(memory_sanitizer)
562 		/* MSan does not instrument manual syscall invocations. */
563 		__msan_unpoison(bytes + read_bytes, n);
564 #  endif
565 		read_bytes += (size_t) n;
566 	}
567 # endif
568 	if (read_bytes < size) {
569 		int    fd = RANDOM_G(random_fd);
570 		struct stat st;
571 
572 		if (fd < 0) {
573 			errno = 0;
574 			fd = open("/dev/urandom", O_RDONLY);
575 			if (fd < 0) {
576 				if (should_throw) {
577 					if (errno != 0) {
578 						zend_throw_exception_ex(random_ce_Random_RandomException, 0, "Cannot open /dev/urandom: %s", strerror(errno));
579 					} else {
580 						zend_throw_exception_ex(random_ce_Random_RandomException, 0, "Cannot open /dev/urandom");
581 					}
582 				}
583 				return FAILURE;
584 			}
585 
586 			errno = 0;
587 			/* Does the file exist and is it a character device? */
588 			if (fstat(fd, &st) != 0 ||
589 # ifdef S_ISNAM
590 					!(S_ISNAM(st.st_mode) || S_ISCHR(st.st_mode))
591 # else
592 					!S_ISCHR(st.st_mode)
593 # endif
594 			) {
595 				close(fd);
596 				if (should_throw) {
597 					if (errno != 0) {
598 						zend_throw_exception_ex(random_ce_Random_RandomException, 0, "Error reading from /dev/urandom: %s", strerror(errno));
599 					} else {
600 						zend_throw_exception_ex(random_ce_Random_RandomException, 0, "Error reading from /dev/urandom");
601 					}
602 				}
603 				return FAILURE;
604 			}
605 			RANDOM_G(random_fd) = fd;
606 		}
607 
608 		read_bytes = 0;
609 		while (read_bytes < size) {
610 			errno = 0;
611 			ssize_t n = read(fd, bytes + read_bytes, size - read_bytes);
612 
613 			if (n <= 0) {
614 				if (should_throw) {
615 					if (errno != 0) {
616 						zend_throw_exception_ex(random_ce_Random_RandomException, 0, "Could not gather sufficient random data: %s", strerror(errno));
617 					} else {
618 						zend_throw_exception_ex(random_ce_Random_RandomException, 0, "Could not gather sufficient random data");
619 					}
620 				}
621 				return FAILURE;
622 			}
623 
624 			read_bytes += (size_t) n;
625 		}
626 	}
627 #endif
628 
629 	return SUCCESS;
630 }
631 /* }}} */
632 
633 /* {{{ php_random_int */
634 PHPAPI int php_random_int(zend_long min, zend_long max, zend_long *result, bool should_throw)
635 {
636 	zend_ulong umax;
637 	zend_ulong trial;
638 
639 	if (min == max) {
640 		*result = min;
641 		return SUCCESS;
642 	}
643 
644 	umax = (zend_ulong) max - (zend_ulong) min;
645 
646 	if (php_random_bytes(&trial, sizeof(trial), should_throw) == FAILURE) {
647 		return FAILURE;
648 	}
649 
650 	/* Special case where no modulus is required */
651 	if (umax == ZEND_ULONG_MAX) {
652 		*result = (zend_long)trial;
653 		return SUCCESS;
654 	}
655 
656 	/* Increment the max so the range is inclusive of max */
657 	umax++;
658 
659 	/* Powers of two are not biased */
660 	if ((umax & (umax - 1)) != 0) {
661 		/* Ceiling under which ZEND_LONG_MAX % max == 0 */
662 		zend_ulong limit = ZEND_ULONG_MAX - (ZEND_ULONG_MAX % umax) - 1;
663 
664 		/* Discard numbers over the limit to avoid modulo bias */
665 		while (trial > limit) {
666 			if (php_random_bytes(&trial, sizeof(trial), should_throw) == FAILURE) {
667 				return FAILURE;
668 			}
669 		}
670 	}
671 
672 	*result = (zend_long)((trial % umax) + min);
673 	return SUCCESS;
674 }
675 /* }}} */
676 
677 /* {{{ Returns a value from the combined linear congruential generator */
678 PHP_FUNCTION(lcg_value)
679 {
680 	ZEND_PARSE_PARAMETERS_NONE();
681 
682 	RETURN_DOUBLE(php_combined_lcg());
683 }
684 /* }}} */
685 
686 /* {{{ Seeds Mersenne Twister random number generator */
687 PHP_FUNCTION(mt_srand)
688 {
689 	zend_long seed = 0;
690 	bool seed_is_null = true;
691 	zend_long mode = MT_RAND_MT19937;
692 	php_random_status *status = RANDOM_G(mt19937);
693 	php_random_status_state_mt19937 *state = status->state;
694 
695 	ZEND_PARSE_PARAMETERS_START(0, 2)
696 		Z_PARAM_OPTIONAL
697 		Z_PARAM_LONG_OR_NULL(seed, seed_is_null)
698 		Z_PARAM_LONG(mode)
699 	ZEND_PARSE_PARAMETERS_END();
700 
701 	state->mode = mode;
702 
703 	if (seed_is_null) {
704 		php_random_mt19937_seed_default(status->state);
705 	} else {
706 		php_random_algo_mt19937.seed(status, (uint64_t) seed);
707 	}
708 	RANDOM_G(mt19937_seeded) = true;
709 }
710 /* }}} */
711 
712 /* {{{ Returns a random number from Mersenne Twister */
713 PHP_FUNCTION(mt_rand)
714 {
715 	zend_long min, max;
716 	int argc = ZEND_NUM_ARGS();
717 
718 	if (argc == 0) {
719 		/* genrand_int31 in mt19937ar.c performs a right shift */
720 		RETURN_LONG(php_mt_rand() >> 1);
721 	}
722 
723 	ZEND_PARSE_PARAMETERS_START(2, 2)
724 		Z_PARAM_LONG(min)
725 		Z_PARAM_LONG(max)
726 	ZEND_PARSE_PARAMETERS_END();
727 
728 	if (UNEXPECTED(max < min)) {
729 		zend_argument_value_error(2, "must be greater than or equal to argument #1 ($min)");
730 		RETURN_THROWS();
731 	}
732 
733 	RETURN_LONG(php_mt_rand_common(min, max));
734 }
735 /* }}} */
736 
737 /* {{{ Returns the maximum value a random number from Mersenne Twister can have */
738 PHP_FUNCTION(mt_getrandmax)
739 {
740 	ZEND_PARSE_PARAMETERS_NONE();
741 
742 	/*
743 	 * Melo: it could be 2^^32 but we only use 2^^31 to maintain
744 	 * compatibility with the previous php_rand
745 	 */
746 	RETURN_LONG(PHP_MT_RAND_MAX); /* 2^^31 */
747 }
748 /* }}} */
749 
750 /* {{{ Returns a random number from Mersenne Twister */
751 PHP_FUNCTION(rand)
752 {
753 	zend_long min, max;
754 	int argc = ZEND_NUM_ARGS();
755 
756 	if (argc == 0) {
757 		/* genrand_int31 in mt19937ar.c performs a right shift */
758 		RETURN_LONG(php_mt_rand() >> 1);
759 	}
760 
761 	ZEND_PARSE_PARAMETERS_START(2, 2)
762 		Z_PARAM_LONG(min)
763 		Z_PARAM_LONG(max)
764 	ZEND_PARSE_PARAMETERS_END();
765 
766 	if (max < min) {
767 		RETURN_LONG(php_mt_rand_common(max, min));
768 	}
769 
770 	RETURN_LONG(php_mt_rand_common(min, max));
771 }
772 /* }}} */
773 
774 /* {{{ Return an arbitrary length of pseudo-random bytes as binary string */
775 PHP_FUNCTION(random_bytes)
776 {
777 	zend_long size;
778 	zend_string *bytes;
779 
780 	ZEND_PARSE_PARAMETERS_START(1, 1)
781 		Z_PARAM_LONG(size)
782 	ZEND_PARSE_PARAMETERS_END();
783 
784 	if (size < 1) {
785 		zend_argument_value_error(1, "must be greater than 0");
786 		RETURN_THROWS();
787 	}
788 
789 	bytes = zend_string_alloc(size, 0);
790 
791 	if (php_random_bytes_throw(ZSTR_VAL(bytes), size) == FAILURE) {
792 		zend_string_release_ex(bytes, 0);
793 		RETURN_THROWS();
794 	}
795 
796 	ZSTR_VAL(bytes)[size] = '\0';
797 
798 	RETURN_STR(bytes);
799 }
800 /* }}} */
801 
802 /* {{{ Return an arbitrary pseudo-random integer */
803 PHP_FUNCTION(random_int)
804 {
805 	zend_long min, max, result;
806 
807 	ZEND_PARSE_PARAMETERS_START(2, 2)
808 		Z_PARAM_LONG(min)
809 		Z_PARAM_LONG(max)
810 	ZEND_PARSE_PARAMETERS_END();
811 
812 	if (min > max) {
813 		zend_argument_value_error(1, "must be less than or equal to argument #2 ($max)");
814 		RETURN_THROWS();
815 	}
816 
817 	if (php_random_int_throw(min, max, &result) == FAILURE) {
818 		RETURN_THROWS();
819 	}
820 
821 	RETURN_LONG(result);
822 }
823 /* }}} */
824 
825 /* {{{ PHP_GINIT_FUNCTION */
826 static PHP_GINIT_FUNCTION(random)
827 {
828 	random_globals->random_fd = -1;
829 
830 	random_globals->combined_lcg = php_random_status_alloc(&php_random_algo_combinedlcg, true);
831 	random_globals->combined_lcg_seeded = false;
832 
833 	random_globals->mt19937 = php_random_status_alloc(&php_random_algo_mt19937, true);
834 	random_globals->mt19937_seeded = false;
835 }
836 /* }}} */
837 
838 /* {{{ PHP_GSHUTDOWN_FUNCTION */
839 static PHP_GSHUTDOWN_FUNCTION(random)
840 {
841 	if (random_globals->random_fd >= 0) {
842 		close(random_globals->random_fd);
843 		random_globals->random_fd = -1;
844 	}
845 
846 	php_random_status_free(random_globals->combined_lcg, true);
847 	random_globals->combined_lcg = NULL;
848 
849 	php_random_status_free(random_globals->mt19937, true);
850 	random_globals->mt19937 = NULL;
851 }
852 /* }}} */
853 
854 /* {{{ PHP_MINIT_FUNCTION */
855 PHP_MINIT_FUNCTION(random)
856 {
857 	/* Random\Engine */
858 	random_ce_Random_Engine = register_class_Random_Engine();
859 
860 	/* Random\CryptoSafeEngine */
861 	random_ce_Random_CryptoSafeEngine = register_class_Random_CryptoSafeEngine(random_ce_Random_Engine);
862 
863 	/* Random\RandomError */
864 	random_ce_Random_RandomError = register_class_Random_RandomError(zend_ce_error);
865 
866 	/* Random\BrokenRandomEngineError */
867 	random_ce_Random_BrokenRandomEngineError = register_class_Random_BrokenRandomEngineError(random_ce_Random_RandomError);
868 
869 	/* Random\RandomException */
870 	random_ce_Random_RandomException = register_class_Random_RandomException(zend_ce_exception);
871 
872 	/* Random\Engine\Mt19937 */
873 	random_ce_Random_Engine_Mt19937 = register_class_Random_Engine_Mt19937(random_ce_Random_Engine);
874 	random_ce_Random_Engine_Mt19937->create_object = php_random_engine_mt19937_new;
875 	memcpy(&random_engine_mt19937_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
876 	random_engine_mt19937_object_handlers.offset = XtOffsetOf(php_random_engine, std);
877 	random_engine_mt19937_object_handlers.free_obj = php_random_engine_common_free_object;
878 	random_engine_mt19937_object_handlers.clone_obj = php_random_engine_common_clone_object;
879 
880 	/* Random\Engine\PcgOnseq128XslRr64 */
881 	random_ce_Random_Engine_PcgOneseq128XslRr64 = register_class_Random_Engine_PcgOneseq128XslRr64(random_ce_Random_Engine);
882 	random_ce_Random_Engine_PcgOneseq128XslRr64->create_object = php_random_engine_pcgoneseq128xslrr64_new;
883 	memcpy(&random_engine_pcgoneseq128xslrr64_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
884 	random_engine_pcgoneseq128xslrr64_object_handlers.offset = XtOffsetOf(php_random_engine, std);
885 	random_engine_pcgoneseq128xslrr64_object_handlers.free_obj = php_random_engine_common_free_object;
886 	random_engine_pcgoneseq128xslrr64_object_handlers.clone_obj = php_random_engine_common_clone_object;
887 
888 	/* Random\Engine\Xoshiro256StarStar */
889 	random_ce_Random_Engine_Xoshiro256StarStar = register_class_Random_Engine_Xoshiro256StarStar(random_ce_Random_Engine);
890 	random_ce_Random_Engine_Xoshiro256StarStar->create_object = php_random_engine_xoshiro256starstar_new;
891 	memcpy(&random_engine_xoshiro256starstar_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
892 	random_engine_xoshiro256starstar_object_handlers.offset = XtOffsetOf(php_random_engine, std);
893 	random_engine_xoshiro256starstar_object_handlers.free_obj = php_random_engine_common_free_object;
894 	random_engine_xoshiro256starstar_object_handlers.clone_obj = php_random_engine_common_clone_object;
895 
896 	/* Random\Engine\Secure */
897 	random_ce_Random_Engine_Secure = register_class_Random_Engine_Secure(random_ce_Random_CryptoSafeEngine);
898 	random_ce_Random_Engine_Secure->create_object = php_random_engine_secure_new;
899 	memcpy(&random_engine_secure_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
900 	random_engine_secure_object_handlers.offset = XtOffsetOf(php_random_engine, std);
901 	random_engine_secure_object_handlers.free_obj = php_random_engine_common_free_object;
902 	random_engine_secure_object_handlers.clone_obj = NULL;
903 
904 	/* Random\Randomizer */
905 	random_ce_Random_Randomizer = register_class_Random_Randomizer();
906 	random_ce_Random_Randomizer->create_object = php_random_randomizer_new;
907 	random_ce_Random_Randomizer->default_object_handlers = &random_randomizer_object_handlers;
908 	memcpy(&random_randomizer_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
909 	random_randomizer_object_handlers.offset = XtOffsetOf(php_random_randomizer, std);
910 	random_randomizer_object_handlers.free_obj = randomizer_free_obj;
911 	random_randomizer_object_handlers.clone_obj = NULL;
912 
913 	/* Random\IntervalBoundary */
914 	random_ce_Random_IntervalBoundary = register_class_Random_IntervalBoundary();
915 
916 	register_random_symbols(module_number);
917 
918 	return SUCCESS;
919 }
920 /* }}} */
921 
922 /* {{{ PHP_RINIT_FUNCTION */
923 PHP_RINIT_FUNCTION(random)
924 {
925 	RANDOM_G(combined_lcg_seeded) = false;
926 	RANDOM_G(mt19937_seeded) = false;
927 
928 	return SUCCESS;
929 }
930 /* }}} */
931 
932 /* {{{ random_module_entry */
933 zend_module_entry random_module_entry = {
934 	STANDARD_MODULE_HEADER,
935 	"random",					/* Extension name */
936 	ext_functions,				/* zend_function_entry */
937 	PHP_MINIT(random),			/* PHP_MINIT - Module initialization */
938 	NULL,						/* PHP_MSHUTDOWN - Module shutdown */
939 	PHP_RINIT(random),			/* PHP_RINIT - Request initialization */
940 	NULL,						/* PHP_RSHUTDOWN - Request shutdown */
941 	NULL,						/* PHP_MINFO - Module info */
942 	PHP_VERSION,				/* Version */
943 	PHP_MODULE_GLOBALS(random),	/* ZTS Module globals */
944 	PHP_GINIT(random),			/* PHP_GINIT - Global initialization */
945 	PHP_GSHUTDOWN(random),		/* PHP_GSHUTDOWN - Global shutdown */
946 	NULL,						/* Post deactivate */
947 	STANDARD_MODULE_PROPERTIES_EX
948 };
949 /* }}} */
950