xref: /PHP-8.4/ext/random/random.c (revision 5c9c2757)
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    |          Tim Düsterhus <timwolla@php.net>                            |
16    +----------------------------------------------------------------------+
17 */
18 
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
22 
23 #include <stdlib.h>
24 #include <sys/stat.h>
25 #include <fcntl.h>
26 #include <math.h>
27 
28 #include "php.h"
29 
30 #include "Zend/zend_attributes.h"
31 #include "Zend/zend_enum.h"
32 #include "Zend/zend_exceptions.h"
33 
34 #include "php_random.h"
35 #include "php_random_csprng.h"
36 #include "ext/standard/sha1.h"
37 
38 #ifdef HAVE_UNISTD_H
39 # include <unistd.h>
40 #endif
41 
42 #ifdef PHP_WIN32
43 # include "win32/time.h"
44 # include "win32/winutil.h"
45 # include <process.h>
46 #else
47 # include <sys/time.h>
48 #endif
49 
50 #ifdef HAVE_SYS_PARAM_H
51 # include <sys/param.h>
52 #endif
53 
54 #include "random_arginfo.h"
55 
56 PHPAPI ZEND_DECLARE_MODULE_GLOBALS(random)
57 
58 PHPAPI zend_class_entry *random_ce_Random_Engine;
59 PHPAPI zend_class_entry *random_ce_Random_CryptoSafeEngine;
60 
61 PHPAPI zend_class_entry *random_ce_Random_Engine_Mt19937;
62 PHPAPI zend_class_entry *random_ce_Random_Engine_PcgOneseq128XslRr64;
63 PHPAPI zend_class_entry *random_ce_Random_Engine_Xoshiro256StarStar;
64 PHPAPI zend_class_entry *random_ce_Random_Engine_Secure;
65 
66 PHPAPI zend_class_entry *random_ce_Random_Randomizer;
67 
68 PHPAPI zend_class_entry *random_ce_Random_IntervalBoundary;
69 
70 PHPAPI zend_class_entry *random_ce_Random_RandomError;
71 PHPAPI zend_class_entry *random_ce_Random_BrokenRandomEngineError;
72 PHPAPI zend_class_entry *random_ce_Random_RandomException;
73 
74 static zend_object_handlers random_engine_mt19937_object_handlers;
75 static zend_object_handlers random_engine_pcgoneseq128xslrr64_object_handlers;
76 static zend_object_handlers random_engine_xoshiro256starstar_object_handlers;
77 static zend_object_handlers random_engine_secure_object_handlers;
78 static zend_object_handlers random_randomizer_object_handlers;
79 
php_random_range32(php_random_algo_with_state engine,uint32_t umax)80 PHPAPI uint32_t php_random_range32(php_random_algo_with_state engine, uint32_t umax)
81 {
82 	const php_random_algo *algo = engine.algo;
83 	void *state = engine.state;
84 
85 	uint32_t result;
86 	size_t total_size;
87 
88 	result = 0;
89 	total_size = 0;
90 	do {
91 		php_random_result r = algo->generate(state);
92 		result = result | (((uint32_t) r.result) << (total_size * 8));
93 		total_size += r.size;
94 		if (EG(exception)) {
95 			return 0;
96 		}
97 	} while (total_size < sizeof(uint32_t));
98 
99 	/* Special case where no modulus is required */
100 	if (UNEXPECTED(umax == UINT32_MAX)) {
101 		return result;
102 	}
103 
104 	/* Increment the max so range is inclusive of max */
105 	umax++;
106 
107 	/* Powers of two are not biased */
108 	if ((umax & (umax - 1)) == 0) {
109 		return result & (umax - 1);
110 	}
111 
112 	/* Ceiling under which UINT32_MAX % max == 0 */
113 	uint32_t limit = UINT32_MAX - (UINT32_MAX % umax) - 1;
114 
115 	/* Discard numbers over the limit to avoid modulo bias */
116 	uint32_t count = 0;
117 	while (UNEXPECTED(result > limit)) {
118 		/* If the requirements cannot be met in a cycles, return fail */
119 		if (++count > PHP_RANDOM_RANGE_ATTEMPTS) {
120 			zend_throw_error(random_ce_Random_BrokenRandomEngineError, "Failed to generate an acceptable random number in %d attempts", PHP_RANDOM_RANGE_ATTEMPTS);
121 			return 0;
122 		}
123 
124 		result = 0;
125 		total_size = 0;
126 		do {
127 			php_random_result r = algo->generate(state);
128 			result = result | (((uint32_t) r.result) << (total_size * 8));
129 			total_size += r.size;
130 			if (EG(exception)) {
131 				return 0;
132 			}
133 		} while (total_size < sizeof(uint32_t));
134 	}
135 
136 	return result % umax;
137 }
138 
php_random_range64(php_random_algo_with_state engine,uint64_t umax)139 PHPAPI uint64_t php_random_range64(php_random_algo_with_state engine, uint64_t umax)
140 {
141 	const php_random_algo *algo = engine.algo;
142 	void *state = engine.state;
143 
144 	uint64_t result;
145 	size_t total_size;
146 
147 	result = 0;
148 	total_size = 0;
149 	do {
150 		php_random_result r = algo->generate(state);
151 		result = result | (r.result << (total_size * 8));
152 		total_size += r.size;
153 		if (EG(exception)) {
154 			return 0;
155 		}
156 	} while (total_size < sizeof(uint64_t));
157 
158 	/* Special case where no modulus is required */
159 	if (UNEXPECTED(umax == UINT64_MAX)) {
160 		return result;
161 	}
162 
163 	/* Increment the max so range is inclusive of max */
164 	umax++;
165 
166 	/* Powers of two are not biased */
167 	if ((umax & (umax - 1)) == 0) {
168 		return result & (umax - 1);
169 	}
170 
171 	/* Ceiling under which UINT64_MAX % max == 0 */
172 	uint64_t limit = UINT64_MAX - (UINT64_MAX % umax) - 1;
173 
174 	/* Discard numbers over the limit to avoid modulo bias */
175 	uint32_t count = 0;
176 	while (UNEXPECTED(result > limit)) {
177 		/* If the requirements cannot be met in a cycles, return fail */
178 		if (++count > PHP_RANDOM_RANGE_ATTEMPTS) {
179 			zend_throw_error(random_ce_Random_BrokenRandomEngineError, "Failed to generate an acceptable random number in %d attempts", PHP_RANDOM_RANGE_ATTEMPTS);
180 			return 0;
181 		}
182 
183 		result = 0;
184 		total_size = 0;
185 		do {
186 			php_random_result r = algo->generate(state);
187 			result = result | (r.result << (total_size * 8));
188 			total_size += r.size;
189 			if (EG(exception)) {
190 				return 0;
191 			}
192 		} while (total_size < sizeof(uint64_t));
193 	}
194 
195 	return result % umax;
196 }
197 
php_random_engine_mt19937_new(zend_class_entry * ce)198 static zend_object *php_random_engine_mt19937_new(zend_class_entry *ce)
199 {
200 	return &php_random_engine_common_init(ce, &random_engine_mt19937_object_handlers, &php_random_algo_mt19937)->std;
201 }
202 
php_random_engine_pcgoneseq128xslrr64_new(zend_class_entry * ce)203 static zend_object *php_random_engine_pcgoneseq128xslrr64_new(zend_class_entry *ce)
204 {
205 	return &php_random_engine_common_init(ce, &random_engine_pcgoneseq128xslrr64_object_handlers, &php_random_algo_pcgoneseq128xslrr64)->std;
206 }
207 
php_random_engine_xoshiro256starstar_new(zend_class_entry * ce)208 static zend_object *php_random_engine_xoshiro256starstar_new(zend_class_entry *ce)
209 {
210 	return &php_random_engine_common_init(ce, &random_engine_xoshiro256starstar_object_handlers, &php_random_algo_xoshiro256starstar)->std;
211 }
212 
php_random_engine_secure_new(zend_class_entry * ce)213 static zend_object *php_random_engine_secure_new(zend_class_entry *ce)
214 {
215 	return &php_random_engine_common_init(ce, &random_engine_secure_object_handlers, &php_random_algo_secure)->std;
216 }
217 
php_random_randomizer_new(zend_class_entry * ce)218 static zend_object *php_random_randomizer_new(zend_class_entry *ce)
219 {
220 	php_random_randomizer *randomizer = zend_object_alloc(sizeof(php_random_randomizer), ce);
221 
222 	zend_object_std_init(&randomizer->std, ce);
223 	object_properties_init(&randomizer->std, ce);
224 
225 	return &randomizer->std;
226 }
227 
randomizer_free_obj(zend_object * object)228 static void randomizer_free_obj(zend_object *object) {
229 	php_random_randomizer *randomizer = php_random_randomizer_from_obj(object);
230 
231 	if (randomizer->is_userland_algo) {
232 		php_random_status_free(randomizer->engine.state, false);
233 	}
234 
235 	zend_object_std_dtor(&randomizer->std);
236 }
237 
php_random_status_alloc(const php_random_algo * algo,const bool persistent)238 PHPAPI void *php_random_status_alloc(const php_random_algo *algo, const bool persistent)
239 {
240 	return algo->state_size > 0 ? pecalloc(1, algo->state_size, persistent) : NULL;
241 }
242 
php_random_status_copy(const php_random_algo * algo,void * old_status,void * new_status)243 PHPAPI void *php_random_status_copy(const php_random_algo *algo, void *old_status, void *new_status)
244 {
245 	return memcpy(new_status, old_status, algo->state_size);
246 }
247 
php_random_status_free(void * status,const bool persistent)248 PHPAPI void php_random_status_free(void *status, const bool persistent)
249 {
250 	pefree(status, persistent);
251 }
252 
php_random_engine_common_init(zend_class_entry * ce,zend_object_handlers * handlers,const php_random_algo * algo)253 PHPAPI php_random_engine *php_random_engine_common_init(zend_class_entry *ce, zend_object_handlers *handlers, const php_random_algo *algo)
254 {
255 	php_random_engine *engine = zend_object_alloc(sizeof(php_random_engine), ce);
256 
257 	zend_object_std_init(&engine->std, ce);
258 	object_properties_init(&engine->std, ce);
259 
260 	engine->engine = (php_random_algo_with_state){
261 		.algo = algo,
262 		.state = php_random_status_alloc(algo, false)
263 	};
264 	engine->std.handlers = handlers;
265 
266 	return engine;
267 }
268 
php_random_engine_common_free_object(zend_object * object)269 PHPAPI void php_random_engine_common_free_object(zend_object *object)
270 {
271 	php_random_engine *engine = php_random_engine_from_obj(object);
272 
273 	php_random_status_free(engine->engine.state, false);
274 	zend_object_std_dtor(object);
275 }
276 
php_random_engine_common_clone_object(zend_object * object)277 PHPAPI zend_object *php_random_engine_common_clone_object(zend_object *object)
278 {
279 	php_random_engine *old_engine = php_random_engine_from_obj(object);
280 	php_random_engine *new_engine = php_random_engine_from_obj(old_engine->std.ce->create_object(old_engine->std.ce));
281 
282 	new_engine->engine.algo = old_engine->engine.algo;
283 	if (old_engine->engine.state) {
284 		new_engine->engine.state = php_random_status_copy(old_engine->engine.algo, old_engine->engine.state, new_engine->engine.state);
285 	}
286 
287 	zend_objects_clone_members(&new_engine->std, &old_engine->std);
288 
289 	return &new_engine->std;
290 }
291 
292 /* {{{ php_random_range */
php_random_range(php_random_algo_with_state engine,zend_long min,zend_long max)293 PHPAPI zend_long php_random_range(php_random_algo_with_state engine, zend_long min, zend_long max)
294 {
295 	zend_ulong umax = (zend_ulong) max - (zend_ulong) min;
296 
297 	if (umax > UINT32_MAX) {
298 		return (zend_long) (php_random_range64(engine, umax) + min);
299 	}
300 
301 	return (zend_long) (php_random_range32(engine, umax) + min);
302 }
303 /* }}} */
304 
305 /* {{{ php_random_default_algo */
php_random_default_algo(void)306 PHPAPI const php_random_algo *php_random_default_algo(void)
307 {
308 	return &php_random_algo_mt19937;
309 }
310 /* }}} */
311 
312 /* {{{ php_random_default_status */
php_random_default_status(void)313 PHPAPI void *php_random_default_status(void)
314 {
315 	php_random_status_state_mt19937 *state = &RANDOM_G(mt19937);
316 
317 	if (!RANDOM_G(mt19937_seeded)) {
318 		state->mode = MT_RAND_MT19937;
319 		php_random_mt19937_seed_default(state);
320 		RANDOM_G(mt19937_seeded) = true;
321 	}
322 
323 	return state;
324 }
325 /* }}} */
326 
327 /* this is read-only, so it's ok */
328 ZEND_SET_ALIGNED(16, static const char hexconvtab[]) = "0123456789abcdef";
329 
330 /* {{{ php_random_bin2hex_le */
331 /* stolen from standard/string.c */
php_random_bin2hex_le(const void * ptr,const size_t len)332 PHPAPI zend_string *php_random_bin2hex_le(const void *ptr, const size_t len)
333 {
334 	zend_string *str;
335 	size_t i;
336 
337 	str = zend_string_safe_alloc(len, 2 * sizeof(char), 0, 0);
338 
339 	i = 0;
340 #ifdef WORDS_BIGENDIAN
341 	/* force little endian */
342 	for (size_t h = len; 0 < h; h--) {
343 		size_t j = h-1;
344 #else
345 	for (size_t j = 0; j < len; j++) {
346 #endif
347 		ZSTR_VAL(str)[i++] = hexconvtab[((unsigned char *) ptr)[j] >> 4];
348 		ZSTR_VAL(str)[i++] = hexconvtab[((unsigned char *) ptr)[j] & 15];
349 	}
350 	ZSTR_VAL(str)[i] = '\0';
351 
352 	return str;
353 }
354 /* }}} */
355 
356 /* {{{ php_random_hex2bin_le */
357 /* stolen from standard/string.c */
358 PHPAPI bool php_random_hex2bin_le(zend_string *hexstr, void *dest)
359 {
360 	size_t len = hexstr->len >> 1;
361 	unsigned char *str = (unsigned char *) hexstr->val, c, l, d;
362 	unsigned char *ptr = (unsigned char *) dest;
363 	int is_letter, i = 0;
364 
365 #ifdef WORDS_BIGENDIAN
366 	/* force little endian */
367 	for (size_t h = len; 0 < h; h--) {
368 		size_t j = h-1;
369 #else
370 	for (size_t j = 0; j < len; j++) {
371 #endif
372 		c = str[i++];
373 		l = c & ~0x20;
374 		is_letter = ((uint32_t) ((l - 'A') ^ (l - 'F' - 1))) >> (8 * sizeof(uint32_t) - 1);
375 
376 		/* basically (c >= '0' && c <= '9') || (l >= 'A' && l <= 'F') */
377 		if (EXPECTED((((c ^ '0') - 10) >> (8 * sizeof(uint32_t) - 1)) | is_letter)) {
378 			d = (l - 0x10 - 0x27 * is_letter) << 4;
379 		} else {
380 			return false;
381 		}
382 		c = str[i++];
383 		l = c & ~0x20;
384 		is_letter = ((uint32_t) ((l - 'A') ^ (l - 'F' - 1))) >> (8 * sizeof(uint32_t) - 1);
385 		if (EXPECTED((((c ^ '0') - 10) >> (8 * sizeof(uint32_t) - 1)) | is_letter)) {
386 			d |= l - 0x10 - 0x27 * is_letter;
387 		} else {
388 			return false;
389 		}
390 		ptr[j] = d;
391 	}
392 	return true;
393 }
394 /* }}} */
395 
396 /* {{{ php_combined_lcg */
397 PHPAPI double php_combined_lcg(void)
398 {
399 	int32_t *state = RANDOM_G(combined_lcg);
400 
401 	if (!RANDOM_G(combined_lcg_seeded)) {
402 		uint64_t seed = 0;
403 
404 		if (php_random_bytes_silent(&seed, sizeof(seed)) == FAILURE) {
405 			seed = php_random_generate_fallback_seed();
406 		}
407 
408 		state[0] = seed & 0xffffffffU;
409 		state[1] = seed >> 32;
410 		RANDOM_G(combined_lcg_seeded) = true;
411 	}
412 
413 	/*
414 	 * combinedLCG() returns a pseudo random number in the range of (0, 1).
415 	 * The function combines two CGs with periods of
416 	 * 2^31 - 85 - 1 and 2^31 - 249 - 1. The period of this function
417 	 * is equal to the product of the two underlying periods, divided
418 	 * by factors shared by the underlying periods, i.e. 2.3 * 10^18.
419 	 *
420 	 * see: https://library.sciencemadness.org/lanl1_a/lib-www/numerica/f7-1.pdf
421 	 */
422 #define PHP_COMBINED_LCG_MODMULT(a, b, c, m, s) q = s / a; s = b * (s - a * q) - c * q; if (s < 0) s += m
423 
424 	int32_t q, z;
425 
426 	/* state[0] = (state[0] * 40014) % 2147483563; */
427 	PHP_COMBINED_LCG_MODMULT(53668, 40014, 12211, 2147483563L, state[0]);
428 	/* state[1] = (state[1] * 40692) % 2147483399; */
429 	PHP_COMBINED_LCG_MODMULT(52774, 40692, 3791, 2147483399L, state[1]);
430 
431 	z = state[0] - state[1];
432 	if (z < 1) {
433 		z += 2147483562;
434 	}
435 
436 	return ((uint64_t)z) * 4.656613e-10;
437 }
438 /* }}} */
439 
440 /* {{{ php_mt_srand */
441 PHPAPI void php_mt_srand(uint32_t seed)
442 {
443 	php_random_mt19937_seed32(php_random_default_status(), seed);
444 }
445 /* }}} */
446 
447 /* {{{ php_mt_rand */
448 PHPAPI uint32_t php_mt_rand(void)
449 {
450 	return (uint32_t) php_random_algo_mt19937.generate(php_random_default_status()).result;
451 }
452 /* }}} */
453 
454 /* {{{ php_mt_rand_range */
455 PHPAPI zend_long php_mt_rand_range(zend_long min, zend_long max)
456 {
457 	return php_random_algo_mt19937.range(php_random_default_status(), min, max);
458 }
459 /* }}} */
460 
461 /* {{{ php_mt_rand_common
462  * rand() allows min > max, mt_rand does not */
463 PHPAPI zend_long php_mt_rand_common(zend_long min, zend_long max)
464 {
465 	php_random_status_state_mt19937 *s = php_random_default_status();
466 
467 	if (s->mode == MT_RAND_MT19937) {
468 		return php_mt_rand_range(min, max);
469 	}
470 
471 	uint64_t r = php_random_algo_mt19937.generate(php_random_default_status()).result >> 1;
472 
473 	/* This is an inlined version of the RAND_RANGE_BADSCALING macro that does not invoke UB when encountering
474 	 * (max - min) > ZEND_LONG_MAX.
475 	 */
476 	zend_ulong offset = (double) ( (double) max - min + 1.0) * (r / (PHP_MT_RAND_MAX + 1.0));
477 
478 	return (zend_long) (offset + min);
479 }
480 /* }}} */
481 
482 /* {{{ Returns a value from the combined linear congruential generator */
483 PHP_FUNCTION(lcg_value)
484 {
485 	ZEND_PARSE_PARAMETERS_NONE();
486 
487 	RETURN_DOUBLE(php_combined_lcg());
488 }
489 /* }}} */
490 
491 /* {{{ Seeds Mersenne Twister random number generator */
492 PHP_FUNCTION(mt_srand)
493 {
494 	zend_long seed = 0;
495 	bool seed_is_null = true;
496 	zend_long mode = MT_RAND_MT19937;
497 	php_random_status_state_mt19937 *state = &RANDOM_G(mt19937);
498 
499 	ZEND_PARSE_PARAMETERS_START(0, 2)
500 		Z_PARAM_OPTIONAL
501 		Z_PARAM_LONG_OR_NULL(seed, seed_is_null)
502 		Z_PARAM_LONG(mode)
503 	ZEND_PARSE_PARAMETERS_END();
504 
505 	switch (mode) {
506 	case MT_RAND_PHP:
507 		state->mode = MT_RAND_PHP;
508 		zend_error(E_DEPRECATED, "The MT_RAND_PHP variant of Mt19937 is deprecated");
509 		break;
510 	default:
511 		state->mode = MT_RAND_MT19937;
512 	}
513 
514 	if (seed_is_null) {
515 		php_random_mt19937_seed_default(state);
516 	} else {
517 		php_random_mt19937_seed32(state, (uint64_t) seed);
518 	}
519 	RANDOM_G(mt19937_seeded) = true;
520 }
521 /* }}} */
522 
523 /* {{{ Returns a random number from Mersenne Twister */
524 PHP_FUNCTION(mt_rand)
525 {
526 	zend_long min, max;
527 	int argc = ZEND_NUM_ARGS();
528 
529 	if (argc == 0) {
530 		/* genrand_int31 in mt19937ar.c performs a right shift */
531 		RETURN_LONG(php_mt_rand() >> 1);
532 	}
533 
534 	ZEND_PARSE_PARAMETERS_START(2, 2)
535 		Z_PARAM_LONG(min)
536 		Z_PARAM_LONG(max)
537 	ZEND_PARSE_PARAMETERS_END();
538 
539 	if (UNEXPECTED(max < min)) {
540 		zend_argument_value_error(2, "must be greater than or equal to argument #1 ($min)");
541 		RETURN_THROWS();
542 	}
543 
544 	RETURN_LONG(php_mt_rand_common(min, max));
545 }
546 /* }}} */
547 
548 /* {{{ Returns the maximum value a random number from Mersenne Twister can have */
549 PHP_FUNCTION(mt_getrandmax)
550 {
551 	ZEND_PARSE_PARAMETERS_NONE();
552 
553 	/*
554 	 * Melo: it could be 2^^32, but we only use 2^^31 to maintain
555 	 * compatibility with the previous php_rand
556 	 */
557 	RETURN_LONG(PHP_MT_RAND_MAX); /* 2^^31 */
558 }
559 /* }}} */
560 
561 /* {{{ Returns a random number from Mersenne Twister */
562 PHP_FUNCTION(rand)
563 {
564 	zend_long min, max;
565 	int argc = ZEND_NUM_ARGS();
566 
567 	if (argc == 0) {
568 		/* genrand_int31 in mt19937ar.c performs a right shift */
569 		RETURN_LONG(php_mt_rand() >> 1);
570 	}
571 
572 	ZEND_PARSE_PARAMETERS_START(2, 2)
573 		Z_PARAM_LONG(min)
574 		Z_PARAM_LONG(max)
575 	ZEND_PARSE_PARAMETERS_END();
576 
577 	if (max < min) {
578 		RETURN_LONG(php_mt_rand_common(max, min));
579 	}
580 
581 	RETURN_LONG(php_mt_rand_common(min, max));
582 }
583 /* }}} */
584 
585 /* {{{ Return an arbitrary length of pseudo-random bytes as binary string */
586 PHP_FUNCTION(random_bytes)
587 {
588 	zend_long size;
589 	zend_string *bytes;
590 
591 	ZEND_PARSE_PARAMETERS_START(1, 1)
592 		Z_PARAM_LONG(size)
593 	ZEND_PARSE_PARAMETERS_END();
594 
595 	if (size < 1) {
596 		zend_argument_value_error(1, "must be greater than 0");
597 		RETURN_THROWS();
598 	}
599 
600 	bytes = zend_string_alloc(size, 0);
601 
602 	if (php_random_bytes_throw(ZSTR_VAL(bytes), size) == FAILURE) {
603 		zend_string_release_ex(bytes, 0);
604 		RETURN_THROWS();
605 	}
606 
607 	ZSTR_VAL(bytes)[size] = '\0';
608 
609 	RETURN_STR(bytes);
610 }
611 /* }}} */
612 
613 /* {{{ Return an arbitrary pseudo-random integer */
614 PHP_FUNCTION(random_int)
615 {
616 	zend_long min, max, result;
617 
618 	ZEND_PARSE_PARAMETERS_START(2, 2)
619 		Z_PARAM_LONG(min)
620 		Z_PARAM_LONG(max)
621 	ZEND_PARSE_PARAMETERS_END();
622 
623 	if (min > max) {
624 		zend_argument_value_error(1, "must be less than or equal to argument #2 ($max)");
625 		RETURN_THROWS();
626 	}
627 
628 	if (php_random_int_throw(min, max, &result) == FAILURE) {
629 		RETURN_THROWS();
630 	}
631 
632 	RETURN_LONG(result);
633 }
634 /* }}} */
635 
636 static inline void fallback_seed_add(PHP_SHA1_CTX *c, void *p, size_t l){
637 	/* Wrapper around PHP_SHA1Update allowing to pass
638 	 * arbitrary pointers without (unsigned char*) casts
639 	 * everywhere.
640 	 */
641 	PHP_SHA1Update(c, p, l);
642 }
643 
644 PHPAPI uint64_t php_random_generate_fallback_seed_ex(php_random_fallback_seed_state *state)
645 {
646 	/* Mix various values using SHA-1 as a PRF to obtain as
647 	 * much entropy as possible, hopefully generating an
648 	 * unpredictable and independent uint64_t. Nevertheless,
649 	 * the output of this function MUST NOT be treated as
650 	 * being cryptographically safe.
651 	 */
652 	PHP_SHA1_CTX c;
653 	struct timeval tv;
654 	void *pointer;
655 	pid_t pid;
656 #ifdef ZTS
657 	THREAD_T tid;
658 #endif
659 	char buf[64 + 1];
660 
661 	PHP_SHA1Init(&c);
662 	if (!state->initialized) {
663 		/* Current time. */
664 		gettimeofday(&tv, NULL);
665 		fallback_seed_add(&c, &tv, sizeof(tv));
666 		/* Various PIDs. */
667 		pid = getpid();
668 		fallback_seed_add(&c, &pid, sizeof(pid));
669 #ifndef PHP_WIN32
670 		pid = getppid();
671 		fallback_seed_add(&c, &pid, sizeof(pid));
672 #endif
673 #ifdef ZTS
674 		tid = tsrm_thread_id();
675 		fallback_seed_add(&c, &tid, sizeof(tid));
676 #endif
677 		/* Pointer values to benefit from ASLR. */
678 		pointer = &state;
679 		fallback_seed_add(&c, &pointer, sizeof(pointer));
680 		pointer = &c;
681 		fallback_seed_add(&c, &pointer, sizeof(pointer));
682 		/* Updated time. */
683 		gettimeofday(&tv, NULL);
684 		fallback_seed_add(&c, &tv, sizeof(tv));
685 		/* Hostname. */
686 		memset(buf, 0, sizeof(buf));
687 		if (gethostname(buf, sizeof(buf) - 1) == 0) {
688 			fallback_seed_add(&c, buf, strlen(buf));
689 		}
690 		/* CSPRNG. */
691 		if (php_random_bytes_silent(buf, 16) == SUCCESS) {
692 			fallback_seed_add(&c, buf, 16);
693 		}
694 		/* Updated time. */
695 		gettimeofday(&tv, NULL);
696 		fallback_seed_add(&c, &tv, sizeof(tv));
697 	} else {
698 		/* Current time. */
699 		gettimeofday(&tv, NULL);
700 		fallback_seed_add(&c, &tv, sizeof(tv));
701 		/* Previous state. */
702 		fallback_seed_add(&c, state->seed, 20);
703 	}
704 	PHP_SHA1Final(state->seed, &c);
705 	state->initialized = true;
706 
707 	uint64_t result = 0;
708 
709 	for (size_t i = 0; i < sizeof(result); i++) {
710 		result = result | (((uint64_t)state->seed[i]) << (i * 8));
711 	}
712 
713 	return result;
714 }
715 
716 PHPAPI uint64_t php_random_generate_fallback_seed(void)
717 {
718 	return php_random_generate_fallback_seed_ex(&RANDOM_G(fallback_seed_state));
719 }
720 
721 /* {{{ PHP_GINIT_FUNCTION */
722 static PHP_GINIT_FUNCTION(random)
723 {
724 	random_globals->fallback_seed_state.initialized = false;
725 }
726 /* }}} */
727 
728 /* {{{ PHP_MINIT_FUNCTION */
729 PHP_MINIT_FUNCTION(random)
730 {
731 	/* Random\Engine */
732 	random_ce_Random_Engine = register_class_Random_Engine();
733 
734 	/* Random\CryptoSafeEngine */
735 	random_ce_Random_CryptoSafeEngine = register_class_Random_CryptoSafeEngine(random_ce_Random_Engine);
736 
737 	/* Random\RandomError */
738 	random_ce_Random_RandomError = register_class_Random_RandomError(zend_ce_error);
739 
740 	/* Random\BrokenRandomEngineError */
741 	random_ce_Random_BrokenRandomEngineError = register_class_Random_BrokenRandomEngineError(random_ce_Random_RandomError);
742 
743 	/* Random\RandomException */
744 	random_ce_Random_RandomException = register_class_Random_RandomException(zend_ce_exception);
745 
746 	/* Random\Engine\Mt19937 */
747 	random_ce_Random_Engine_Mt19937 = register_class_Random_Engine_Mt19937(random_ce_Random_Engine);
748 	random_ce_Random_Engine_Mt19937->create_object = php_random_engine_mt19937_new;
749 	memcpy(&random_engine_mt19937_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
750 	random_engine_mt19937_object_handlers.offset = XtOffsetOf(php_random_engine, std);
751 	random_engine_mt19937_object_handlers.free_obj = php_random_engine_common_free_object;
752 	random_engine_mt19937_object_handlers.clone_obj = php_random_engine_common_clone_object;
753 
754 	/* Random\Engine\PcgOnseq128XslRr64 */
755 	random_ce_Random_Engine_PcgOneseq128XslRr64 = register_class_Random_Engine_PcgOneseq128XslRr64(random_ce_Random_Engine);
756 	random_ce_Random_Engine_PcgOneseq128XslRr64->create_object = php_random_engine_pcgoneseq128xslrr64_new;
757 	memcpy(&random_engine_pcgoneseq128xslrr64_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
758 	random_engine_pcgoneseq128xslrr64_object_handlers.offset = XtOffsetOf(php_random_engine, std);
759 	random_engine_pcgoneseq128xslrr64_object_handlers.free_obj = php_random_engine_common_free_object;
760 	random_engine_pcgoneseq128xslrr64_object_handlers.clone_obj = php_random_engine_common_clone_object;
761 
762 	/* Random\Engine\Xoshiro256StarStar */
763 	random_ce_Random_Engine_Xoshiro256StarStar = register_class_Random_Engine_Xoshiro256StarStar(random_ce_Random_Engine);
764 	random_ce_Random_Engine_Xoshiro256StarStar->create_object = php_random_engine_xoshiro256starstar_new;
765 	memcpy(&random_engine_xoshiro256starstar_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
766 	random_engine_xoshiro256starstar_object_handlers.offset = XtOffsetOf(php_random_engine, std);
767 	random_engine_xoshiro256starstar_object_handlers.free_obj = php_random_engine_common_free_object;
768 	random_engine_xoshiro256starstar_object_handlers.clone_obj = php_random_engine_common_clone_object;
769 
770 	/* Random\Engine\Secure */
771 	random_ce_Random_Engine_Secure = register_class_Random_Engine_Secure(random_ce_Random_CryptoSafeEngine);
772 	random_ce_Random_Engine_Secure->create_object = php_random_engine_secure_new;
773 	memcpy(&random_engine_secure_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
774 	random_engine_secure_object_handlers.offset = XtOffsetOf(php_random_engine, std);
775 	random_engine_secure_object_handlers.free_obj = php_random_engine_common_free_object;
776 	random_engine_secure_object_handlers.clone_obj = NULL;
777 
778 	/* Random\Randomizer */
779 	random_ce_Random_Randomizer = register_class_Random_Randomizer();
780 	random_ce_Random_Randomizer->create_object = php_random_randomizer_new;
781 	random_ce_Random_Randomizer->default_object_handlers = &random_randomizer_object_handlers;
782 	memcpy(&random_randomizer_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
783 	random_randomizer_object_handlers.offset = XtOffsetOf(php_random_randomizer, std);
784 	random_randomizer_object_handlers.free_obj = randomizer_free_obj;
785 	random_randomizer_object_handlers.clone_obj = NULL;
786 
787 	/* Random\IntervalBoundary */
788 	random_ce_Random_IntervalBoundary = register_class_Random_IntervalBoundary();
789 
790 	register_random_symbols(module_number);
791 
792 	return SUCCESS;
793 }
794 /* }}} */
795 
796 /* {{{ PHP_MSHUTDOWN_FUNCTION */
797 PHP_MSHUTDOWN_FUNCTION(random)
798 {
799 	php_random_csprng_shutdown();
800 
801 	return SUCCESS;
802 }
803 /* }}} */
804 
805 /* {{{ PHP_RINIT_FUNCTION */
806 PHP_RINIT_FUNCTION(random)
807 {
808 	RANDOM_G(combined_lcg_seeded) = false;
809 	RANDOM_G(mt19937_seeded) = false;
810 
811 	return SUCCESS;
812 }
813 /* }}} */
814 
815 /* {{{ random_module_entry */
816 zend_module_entry random_module_entry = {
817 	STANDARD_MODULE_HEADER,
818 	"random",					/* Extension name */
819 	ext_functions,				/* zend_function_entry */
820 	PHP_MINIT(random),			/* PHP_MINIT - Module initialization */
821 	PHP_MSHUTDOWN(random),		/* PHP_MSHUTDOWN - Module shutdown */
822 	PHP_RINIT(random),			/* PHP_RINIT - Request initialization */
823 	NULL,						/* PHP_RSHUTDOWN - Request shutdown */
824 	NULL,						/* PHP_MINFO - Module info */
825 	PHP_VERSION,				/* Version */
826 	PHP_MODULE_GLOBALS(random),	/* ZTS Module globals */
827 	PHP_GINIT(random),			/* PHP_GINIT - Global initialization */
828 	NULL,						/* PHP_GSHUTDOWN - Global shutdown */
829 	NULL,						/* Post deactivate */
830 	STANDARD_MODULE_PROPERTIES_EX
831 };
832 /* }}} */
833