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