xref: /PHP-8.2/ext/random/random.c (revision 97b3b455)
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 #include "php_random_csprng.h"
34 
35 #if HAVE_UNISTD_H
36 # include <unistd.h>
37 #endif
38 
39 #ifdef PHP_WIN32
40 # include "win32/time.h"
41 # include "win32/winutil.h"
42 # include <process.h>
43 #else
44 # include <sys/time.h>
45 #endif
46 
47 #if HAVE_SYS_PARAM_H
48 # include <sys/param.h>
49 #endif
50 
51 #include "random_arginfo.h"
52 
53 PHPAPI ZEND_DECLARE_MODULE_GLOBALS(random)
54 
55 PHPAPI zend_class_entry *random_ce_Random_Engine;
56 PHPAPI zend_class_entry *random_ce_Random_CryptoSafeEngine;
57 
58 PHPAPI zend_class_entry *random_ce_Random_Engine_Mt19937;
59 PHPAPI zend_class_entry *random_ce_Random_Engine_PcgOneseq128XslRr64;
60 PHPAPI zend_class_entry *random_ce_Random_Engine_Xoshiro256StarStar;
61 PHPAPI zend_class_entry *random_ce_Random_Engine_Secure;
62 
63 PHPAPI zend_class_entry *random_ce_Random_Randomizer;
64 
65 PHPAPI zend_class_entry *random_ce_Random_IntervalBoundary;
66 
67 PHPAPI zend_class_entry *random_ce_Random_RandomError;
68 PHPAPI zend_class_entry *random_ce_Random_BrokenRandomEngineError;
69 PHPAPI zend_class_entry *random_ce_Random_RandomException;
70 
71 static zend_object_handlers random_engine_mt19937_object_handlers;
72 static zend_object_handlers random_engine_pcgoneseq128xslrr64_object_handlers;
73 static zend_object_handlers random_engine_xoshiro256starstar_object_handlers;
74 static zend_object_handlers random_engine_secure_object_handlers;
75 static zend_object_handlers random_randomizer_object_handlers;
76 
php_random_range32(const php_random_algo * algo,php_random_status * status,uint32_t umax)77 PHPAPI uint32_t php_random_range32(const php_random_algo *algo, php_random_status *status, uint32_t umax)
78 {
79 	uint32_t result, limit;
80 	size_t total_size = 0;
81 	uint32_t count = 0;
82 
83 	result = 0;
84 	total_size = 0;
85 	do {
86 		php_random_result r = algo->generate(status);
87 		result = result | (((uint32_t) r.result) << (total_size * 8));
88 		total_size += r.size;
89 		if (EG(exception)) {
90 			return 0;
91 		}
92 	} while (total_size < sizeof(uint32_t));
93 
94 	/* Special case where no modulus is required */
95 	if (UNEXPECTED(umax == UINT32_MAX)) {
96 		return result;
97 	}
98 
99 	/* Increment the max so range is inclusive of max */
100 	umax++;
101 
102 	/* Powers of two are not biased */
103 	if ((umax & (umax - 1)) == 0) {
104 		return result & (umax - 1);
105 	}
106 
107 	/* Ceiling under which UINT32_MAX % max == 0 */
108 	limit = UINT32_MAX - (UINT32_MAX % umax) - 1;
109 
110 	/* Discard numbers over the limit to avoid modulo bias */
111 	while (UNEXPECTED(result > limit)) {
112 		/* If the requirements cannot be met in a cycles, return fail */
113 		if (++count > PHP_RANDOM_RANGE_ATTEMPTS) {
114 			zend_throw_error(random_ce_Random_BrokenRandomEngineError, "Failed to generate an acceptable random number in %d attempts", PHP_RANDOM_RANGE_ATTEMPTS);
115 			return 0;
116 		}
117 
118 		result = 0;
119 		total_size = 0;
120 		do {
121 			php_random_result r = algo->generate(status);
122 			result = result | (((uint32_t) r.result) << (total_size * 8));
123 			total_size += r.size;
124 			if (EG(exception)) {
125 				return 0;
126 			}
127 		} while (total_size < sizeof(uint32_t));
128 	}
129 
130 	return result % umax;
131 }
132 
php_random_range64(const php_random_algo * algo,php_random_status * status,uint64_t umax)133 PHPAPI uint64_t php_random_range64(const php_random_algo *algo, php_random_status *status, uint64_t umax)
134 {
135 	uint64_t result, limit;
136 	size_t total_size = 0;
137 	uint32_t count = 0;
138 
139 	result = 0;
140 	total_size = 0;
141 	do {
142 		php_random_result r = algo->generate(status);
143 		result = result | (r.result << (total_size * 8));
144 		total_size += r.size;
145 		if (EG(exception)) {
146 			return 0;
147 		}
148 	} while (total_size < sizeof(uint64_t));
149 
150 	/* Special case where no modulus is required */
151 	if (UNEXPECTED(umax == UINT64_MAX)) {
152 		return result;
153 	}
154 
155 	/* Increment the max so range is inclusive of max */
156 	umax++;
157 
158 	/* Powers of two are not biased */
159 	if ((umax & (umax - 1)) == 0) {
160 		return result & (umax - 1);
161 	}
162 
163 	/* Ceiling under which UINT64_MAX % max == 0 */
164 	limit = UINT64_MAX - (UINT64_MAX % umax) - 1;
165 
166 	/* Discard numbers over the limit to avoid modulo bias */
167 	while (UNEXPECTED(result > limit)) {
168 		/* If the requirements cannot be met in a cycles, return fail */
169 		if (++count > PHP_RANDOM_RANGE_ATTEMPTS) {
170 			zend_throw_error(random_ce_Random_BrokenRandomEngineError, "Failed to generate an acceptable random number in %d attempts", PHP_RANDOM_RANGE_ATTEMPTS);
171 			return 0;
172 		}
173 
174 		result = 0;
175 		total_size = 0;
176 		do {
177 			php_random_result r = algo->generate(status);
178 			result = result | (r.result << (total_size * 8));
179 			total_size += r.size;
180 			if (EG(exception)) {
181 				return 0;
182 			}
183 		} while (total_size < sizeof(uint64_t));
184 	}
185 
186 	return result % umax;
187 }
188 
php_random_engine_mt19937_new(zend_class_entry * ce)189 static zend_object *php_random_engine_mt19937_new(zend_class_entry *ce)
190 {
191 	return &php_random_engine_common_init(ce, &random_engine_mt19937_object_handlers, &php_random_algo_mt19937)->std;
192 }
193 
php_random_engine_pcgoneseq128xslrr64_new(zend_class_entry * ce)194 static zend_object *php_random_engine_pcgoneseq128xslrr64_new(zend_class_entry *ce)
195 {
196 	return &php_random_engine_common_init(ce, &random_engine_pcgoneseq128xslrr64_object_handlers, &php_random_algo_pcgoneseq128xslrr64)->std;
197 }
198 
php_random_engine_xoshiro256starstar_new(zend_class_entry * ce)199 static zend_object *php_random_engine_xoshiro256starstar_new(zend_class_entry *ce)
200 {
201 	return &php_random_engine_common_init(ce, &random_engine_xoshiro256starstar_object_handlers, &php_random_algo_xoshiro256starstar)->std;
202 }
203 
php_random_engine_secure_new(zend_class_entry * ce)204 static zend_object *php_random_engine_secure_new(zend_class_entry *ce)
205 {
206 	return &php_random_engine_common_init(ce, &random_engine_secure_object_handlers, &php_random_algo_secure)->std;
207 }
208 
php_random_randomizer_new(zend_class_entry * ce)209 static zend_object *php_random_randomizer_new(zend_class_entry *ce)
210 {
211 	php_random_randomizer *randomizer = zend_object_alloc(sizeof(php_random_randomizer), ce);
212 
213 	zend_object_std_init(&randomizer->std, ce);
214 	object_properties_init(&randomizer->std, ce);
215 
216 	return &randomizer->std;
217 }
218 
randomizer_free_obj(zend_object * object)219 static void randomizer_free_obj(zend_object *object) {
220 	php_random_randomizer *randomizer = php_random_randomizer_from_obj(object);
221 
222 	if (randomizer->is_userland_algo) {
223 		php_random_status_free(randomizer->status, false);
224 	}
225 
226 	zend_object_std_dtor(&randomizer->std);
227 }
228 
php_random_status_alloc(const php_random_algo * algo,const bool persistent)229 PHPAPI php_random_status *php_random_status_alloc(const php_random_algo *algo, const bool persistent)
230 {
231 	php_random_status *status = pecalloc(1, sizeof(php_random_status), persistent);
232 
233 	status->state = algo->state_size > 0 ? pecalloc(1, algo->state_size, persistent) : NULL;
234 
235 	return status;
236 }
237 
php_random_status_copy(const php_random_algo * algo,php_random_status * old_status,php_random_status * new_status)238 PHPAPI php_random_status *php_random_status_copy(const php_random_algo *algo, php_random_status *old_status, php_random_status *new_status)
239 {
240 	new_status->state = memcpy(new_status->state, old_status->state, algo->state_size);
241 
242 	return new_status;
243 }
244 
php_random_status_free(php_random_status * status,const bool persistent)245 PHPAPI void php_random_status_free(php_random_status *status, const bool persistent)
246 {
247 	if (status != NULL) {
248 		pefree(status->state, persistent);
249 	}
250 
251 	pefree(status, persistent);
252 }
253 
php_random_engine_common_init(zend_class_entry * ce,zend_object_handlers * handlers,const php_random_algo * algo)254 PHPAPI php_random_engine *php_random_engine_common_init(zend_class_entry *ce, zend_object_handlers *handlers, const php_random_algo *algo)
255 {
256 	php_random_engine *engine = zend_object_alloc(sizeof(php_random_engine), ce);
257 
258 	zend_object_std_init(&engine->std, ce);
259 	object_properties_init(&engine->std, ce);
260 
261 	engine->algo = algo;
262 	engine->status = php_random_status_alloc(engine->algo, false);
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->status, 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->algo = old_engine->algo;
282 	if (old_engine->status) {
283 		new_engine->status = php_random_status_copy(old_engine->algo, old_engine->status, new_engine->status);
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(const php_random_algo * algo,php_random_status * status,zend_long min,zend_long max)292 PHPAPI zend_long php_random_range(const php_random_algo *algo, php_random_status *status, 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(algo, status, umax) + min);
298 	}
299 
300 	return (zend_long) (php_random_range32(algo, status, 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 php_random_status *php_random_default_status(void)
313 {
314 	php_random_status *status = RANDOM_G(mt19937);
315 
316 	if (!RANDOM_G(mt19937_seeded)) {
317 		php_random_mt19937_seed_default(status->state);
318 		RANDOM_G(mt19937_seeded) = true;
319 	}
320 
321 	return status;
322 }
323 /* }}} */
324 
325 /* this is read-only, so it's ok */
326 ZEND_SET_ALIGNED(16, static const char hexconvtab[]) = "0123456789abcdef";
327 
328 /* {{{ php_random_bin2hex_le */
329 /* stolen from standard/string.c */
php_random_bin2hex_le(const void * ptr,const size_t len)330 PHPAPI zend_string *php_random_bin2hex_le(const void *ptr, const size_t len)
331 {
332 	zend_string *str;
333 	size_t i;
334 
335 	str = zend_string_safe_alloc(len, 2 * sizeof(char), 0, 0);
336 
337 	i = 0;
338 #ifdef WORDS_BIGENDIAN
339 	/* force little endian */
340 	for (zend_long j = (len - 1); 0 <= j; j--) {
341 #else
342 	for (zend_long j = 0; j < len; j++) {
343 #endif
344 		ZSTR_VAL(str)[i++] = hexconvtab[((unsigned char *) ptr)[j] >> 4];
345 		ZSTR_VAL(str)[i++] = hexconvtab[((unsigned char *) ptr)[j] & 15];
346 	}
347 	ZSTR_VAL(str)[i] = '\0';
348 
349 	return str;
350 }
351 /* }}} */
352 
353 /* {{{ php_random_hex2bin_le */
354 /* stolen from standard/string.c */
355 PHPAPI bool php_random_hex2bin_le(zend_string *hexstr, void *dest)
356 {
357 	size_t len = hexstr->len >> 1;
358 	unsigned char *str = (unsigned char *) hexstr->val, c, l, d;
359 	unsigned char *ptr = (unsigned char *) dest;
360 	int is_letter, i = 0;
361 
362 #ifdef WORDS_BIGENDIAN
363 	/* force little endian */
364 	for (zend_long j = (len - 1); 0 <= j; j--) {
365 #else
366 	for (zend_long j = 0; j < len; j++) {
367 #endif
368 		c = str[i++];
369 		l = c & ~0x20;
370 		is_letter = ((uint32_t) ((l - 'A') ^ (l - 'F' - 1))) >> (8 * sizeof(uint32_t) - 1);
371 
372 		/* basically (c >= '0' && c <= '9') || (l >= 'A' && l <= 'F') */
373 		if (EXPECTED((((c ^ '0') - 10) >> (8 * sizeof(uint32_t) - 1)) | is_letter)) {
374 			d = (l - 0x10 - 0x27 * is_letter) << 4;
375 		} else {
376 			return false;
377 		}
378 		c = str[i++];
379 		l = c & ~0x20;
380 		is_letter = ((uint32_t) ((l - 'A') ^ (l - 'F' - 1))) >> (8 * sizeof(uint32_t) - 1);
381 		if (EXPECTED((((c ^ '0') - 10) >> (8 * sizeof(uint32_t) - 1)) | is_letter)) {
382 			d |= l - 0x10 - 0x27 * is_letter;
383 		} else {
384 			return false;
385 		}
386 		ptr[j] = d;
387 	}
388 	return true;
389 }
390 /* }}} */
391 
392 /* {{{ php_combined_lcg */
393 PHPAPI double php_combined_lcg(void)
394 {
395 	php_random_status *status = RANDOM_G(combined_lcg);
396 
397 	if (!RANDOM_G(combined_lcg_seeded)) {
398 		php_random_combinedlcg_seed_default(status->state);
399 		RANDOM_G(combined_lcg_seeded) = true;
400 	}
401 
402 	return php_random_algo_combinedlcg.generate(status).result * 4.656613e-10;
403 }
404 /* }}} */
405 
406 /* {{{ php_mt_srand */
407 PHPAPI void php_mt_srand(uint32_t seed)
408 {
409 	/* Seed the generator with a simple uint32 */
410 	php_random_algo_mt19937.seed(php_random_default_status(), (zend_long) 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 *status = php_random_default_status();
433 	php_random_status_state_mt19937 *s = status->state;
434 
435 	if (s->mode == MT_RAND_MT19937) {
436 		return php_mt_rand_range(min, max);
437 	}
438 
439 	uint64_t r = php_random_algo_mt19937.generate(php_random_default_status()).result >> 1;
440 
441 	/* This is an inlined version of the RAND_RANGE_BADSCALING macro that does not invoke UB when encountering
442 	 * (max - min) > ZEND_LONG_MAX.
443 	 */
444 	zend_ulong offset = (double) ( (double) max - min + 1.0) * (r / (PHP_MT_RAND_MAX + 1.0));
445 
446 	return (zend_long) (offset + min);
447 }
448 /* }}} */
449 
450 /* {{{ php_srand */
451 PHPAPI void php_srand(zend_long seed)
452 {
453 	php_mt_srand((uint32_t) seed);
454 }
455 /* }}} */
456 
457 /* {{{ php_rand */
458 PHPAPI zend_long php_rand(void)
459 {
460 	return php_mt_rand();
461 }
462 /* }}} */
463 
464 /* {{{ Returns a value from the combined linear congruential generator */
465 PHP_FUNCTION(lcg_value)
466 {
467 	ZEND_PARSE_PARAMETERS_NONE();
468 
469 	RETURN_DOUBLE(php_combined_lcg());
470 }
471 /* }}} */
472 
473 /* {{{ Seeds Mersenne Twister random number generator */
474 PHP_FUNCTION(mt_srand)
475 {
476 	zend_long seed = 0;
477 	bool seed_is_null = true;
478 	zend_long mode = MT_RAND_MT19937;
479 	php_random_status *status = RANDOM_G(mt19937);
480 	php_random_status_state_mt19937 *state = status->state;
481 
482 	ZEND_PARSE_PARAMETERS_START(0, 2)
483 		Z_PARAM_OPTIONAL
484 		Z_PARAM_LONG_OR_NULL(seed, seed_is_null)
485 		Z_PARAM_LONG(mode)
486 	ZEND_PARSE_PARAMETERS_END();
487 
488 	state->mode = mode;
489 
490 	/* Anything that is not MT_RAND_MT19937 was interpreted as MT_RAND_PHP. */
491 	if (state->mode != MT_RAND_MT19937) {
492 		zend_error(E_DEPRECATED, "The MT_RAND_PHP variant of Mt19937 is deprecated");
493 	}
494 
495 	if (seed_is_null) {
496 		php_random_mt19937_seed_default(status->state);
497 	} else {
498 		php_random_algo_mt19937.seed(status, (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 /* {{{ PHP_GINIT_FUNCTION */
618 static PHP_GINIT_FUNCTION(random)
619 {
620 	random_globals->random_fd = -1;
621 
622 	random_globals->combined_lcg = php_random_status_alloc(&php_random_algo_combinedlcg, true);
623 	random_globals->combined_lcg_seeded = false;
624 
625 	random_globals->mt19937 = php_random_status_alloc(&php_random_algo_mt19937, true);
626 	random_globals->mt19937_seeded = false;
627 }
628 /* }}} */
629 
630 /* {{{ PHP_GSHUTDOWN_FUNCTION */
631 static PHP_GSHUTDOWN_FUNCTION(random)
632 {
633 	if (random_globals->random_fd >= 0) {
634 		close(random_globals->random_fd);
635 		random_globals->random_fd = -1;
636 	}
637 
638 	php_random_status_free(random_globals->combined_lcg, true);
639 	random_globals->combined_lcg = NULL;
640 
641 	php_random_status_free(random_globals->mt19937, true);
642 	random_globals->mt19937 = NULL;
643 }
644 /* }}} */
645 
646 /* {{{ PHP_MINIT_FUNCTION */
647 PHP_MINIT_FUNCTION(random)
648 {
649 	/* Random\Engine */
650 	random_ce_Random_Engine = register_class_Random_Engine();
651 
652 	/* Random\CryptoSafeEngine */
653 	random_ce_Random_CryptoSafeEngine = register_class_Random_CryptoSafeEngine(random_ce_Random_Engine);
654 
655 	/* Random\RandomError */
656 	random_ce_Random_RandomError = register_class_Random_RandomError(zend_ce_error);
657 
658 	/* Random\BrokenRandomEngineError */
659 	random_ce_Random_BrokenRandomEngineError = register_class_Random_BrokenRandomEngineError(random_ce_Random_RandomError);
660 
661 	/* Random\RandomException */
662 	random_ce_Random_RandomException = register_class_Random_RandomException(zend_ce_exception);
663 
664 	/* Random\Engine\Mt19937 */
665 	random_ce_Random_Engine_Mt19937 = register_class_Random_Engine_Mt19937(random_ce_Random_Engine);
666 	random_ce_Random_Engine_Mt19937->create_object = php_random_engine_mt19937_new;
667 	memcpy(&random_engine_mt19937_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
668 	random_engine_mt19937_object_handlers.offset = XtOffsetOf(php_random_engine, std);
669 	random_engine_mt19937_object_handlers.free_obj = php_random_engine_common_free_object;
670 	random_engine_mt19937_object_handlers.clone_obj = php_random_engine_common_clone_object;
671 
672 	/* Random\Engine\PcgOnseq128XslRr64 */
673 	random_ce_Random_Engine_PcgOneseq128XslRr64 = register_class_Random_Engine_PcgOneseq128XslRr64(random_ce_Random_Engine);
674 	random_ce_Random_Engine_PcgOneseq128XslRr64->create_object = php_random_engine_pcgoneseq128xslrr64_new;
675 	memcpy(&random_engine_pcgoneseq128xslrr64_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
676 	random_engine_pcgoneseq128xslrr64_object_handlers.offset = XtOffsetOf(php_random_engine, std);
677 	random_engine_pcgoneseq128xslrr64_object_handlers.free_obj = php_random_engine_common_free_object;
678 	random_engine_pcgoneseq128xslrr64_object_handlers.clone_obj = php_random_engine_common_clone_object;
679 
680 	/* Random\Engine\Xoshiro256StarStar */
681 	random_ce_Random_Engine_Xoshiro256StarStar = register_class_Random_Engine_Xoshiro256StarStar(random_ce_Random_Engine);
682 	random_ce_Random_Engine_Xoshiro256StarStar->create_object = php_random_engine_xoshiro256starstar_new;
683 	memcpy(&random_engine_xoshiro256starstar_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
684 	random_engine_xoshiro256starstar_object_handlers.offset = XtOffsetOf(php_random_engine, std);
685 	random_engine_xoshiro256starstar_object_handlers.free_obj = php_random_engine_common_free_object;
686 	random_engine_xoshiro256starstar_object_handlers.clone_obj = php_random_engine_common_clone_object;
687 
688 	/* Random\Engine\Secure */
689 	random_ce_Random_Engine_Secure = register_class_Random_Engine_Secure(random_ce_Random_CryptoSafeEngine);
690 	random_ce_Random_Engine_Secure->create_object = php_random_engine_secure_new;
691 	memcpy(&random_engine_secure_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
692 	random_engine_secure_object_handlers.offset = XtOffsetOf(php_random_engine, std);
693 	random_engine_secure_object_handlers.free_obj = php_random_engine_common_free_object;
694 	random_engine_secure_object_handlers.clone_obj = NULL;
695 
696 	/* Random\Randomizer */
697 	random_ce_Random_Randomizer = register_class_Random_Randomizer();
698 	random_ce_Random_Randomizer->create_object = php_random_randomizer_new;
699 	random_ce_Random_Randomizer->default_object_handlers = &random_randomizer_object_handlers;
700 	memcpy(&random_randomizer_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
701 	random_randomizer_object_handlers.offset = XtOffsetOf(php_random_randomizer, std);
702 	random_randomizer_object_handlers.free_obj = randomizer_free_obj;
703 	random_randomizer_object_handlers.clone_obj = NULL;
704 
705 	/* Random\IntervalBoundary */
706 	random_ce_Random_IntervalBoundary = register_class_Random_IntervalBoundary();
707 
708 	register_random_symbols(module_number);
709 
710 	return SUCCESS;
711 }
712 /* }}} */
713 
714 /* {{{ PHP_RINIT_FUNCTION */
715 PHP_RINIT_FUNCTION(random)
716 {
717 	RANDOM_G(combined_lcg_seeded) = false;
718 	RANDOM_G(mt19937_seeded) = false;
719 
720 	return SUCCESS;
721 }
722 /* }}} */
723 
724 /* {{{ random_module_entry */
725 zend_module_entry random_module_entry = {
726 	STANDARD_MODULE_HEADER,
727 	"random",					/* Extension name */
728 	ext_functions,				/* zend_function_entry */
729 	PHP_MINIT(random),			/* PHP_MINIT - Module initialization */
730 	NULL,						/* PHP_MSHUTDOWN - Module shutdown */
731 	PHP_RINIT(random),			/* PHP_RINIT - Request initialization */
732 	NULL,						/* PHP_RSHUTDOWN - Request shutdown */
733 	NULL,						/* PHP_MINFO - Module info */
734 	PHP_VERSION,				/* Version */
735 	PHP_MODULE_GLOBALS(random),	/* ZTS Module globals */
736 	PHP_GINIT(random),			/* PHP_GINIT - Global initialization */
737 	PHP_GSHUTDOWN(random),		/* PHP_GSHUTDOWN - Global shutdown */
738 	NULL,						/* Post deactivate */
739 	STANDARD_MODULE_PROPERTIES_EX
740 };
741 /* }}} */
742