xref: /PHP-8.3/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    +----------------------------------------------------------------------+
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 #if HAVE_SYS_PARAM_H
47 # include <sys/param.h>
48 #endif
49 
50 #include "random_arginfo.h"
51 
52 PHPAPI ZEND_DECLARE_MODULE_GLOBALS(random)
53 
54 PHPAPI zend_class_entry *random_ce_Random_Engine;
55 PHPAPI zend_class_entry *random_ce_Random_CryptoSafeEngine;
56 
57 PHPAPI zend_class_entry *random_ce_Random_Engine_Mt19937;
58 PHPAPI zend_class_entry *random_ce_Random_Engine_PcgOneseq128XslRr64;
59 PHPAPI zend_class_entry *random_ce_Random_Engine_Xoshiro256StarStar;
60 PHPAPI zend_class_entry *random_ce_Random_Engine_Secure;
61 
62 PHPAPI zend_class_entry *random_ce_Random_Randomizer;
63 
64 PHPAPI zend_class_entry *random_ce_Random_IntervalBoundary;
65 
66 PHPAPI zend_class_entry *random_ce_Random_RandomError;
67 PHPAPI zend_class_entry *random_ce_Random_BrokenRandomEngineError;
68 PHPAPI zend_class_entry *random_ce_Random_RandomException;
69 
70 static zend_object_handlers random_engine_mt19937_object_handlers;
71 static zend_object_handlers random_engine_pcgoneseq128xslrr64_object_handlers;
72 static zend_object_handlers random_engine_xoshiro256starstar_object_handlers;
73 static zend_object_handlers random_engine_secure_object_handlers;
74 static zend_object_handlers random_randomizer_object_handlers;
75 
php_random_range32(const php_random_algo * algo,php_random_status * status,uint32_t umax)76 PHPAPI uint32_t php_random_range32(const php_random_algo *algo, php_random_status *status, uint32_t umax)
77 {
78 	uint32_t result, limit;
79 	size_t total_size = 0;
80 	uint32_t count = 0;
81 
82 	result = 0;
83 	total_size = 0;
84 	do {
85 		uint32_t r = algo->generate(status);
86 		result = result | (r << (total_size * 8));
87 		total_size += status->last_generated_size;
88 		if (EG(exception)) {
89 			return 0;
90 		}
91 	} while (total_size < sizeof(uint32_t));
92 
93 	/* Special case where no modulus is required */
94 	if (UNEXPECTED(umax == UINT32_MAX)) {
95 		return result;
96 	}
97 
98 	/* Increment the max so range is inclusive of max */
99 	umax++;
100 
101 	/* Powers of two are not biased */
102 	if ((umax & (umax - 1)) == 0) {
103 		return result & (umax - 1);
104 	}
105 
106 	/* Ceiling under which UINT32_MAX % max == 0 */
107 	limit = UINT32_MAX - (UINT32_MAX % umax) - 1;
108 
109 	/* Discard numbers over the limit to avoid modulo bias */
110 	while (UNEXPECTED(result > limit)) {
111 		/* If the requirements cannot be met in a cycles, return fail */
112 		if (++count > PHP_RANDOM_RANGE_ATTEMPTS) {
113 			zend_throw_error(random_ce_Random_BrokenRandomEngineError, "Failed to generate an acceptable random number in %d attempts", PHP_RANDOM_RANGE_ATTEMPTS);
114 			return 0;
115 		}
116 
117 		result = 0;
118 		total_size = 0;
119 		do {
120 			uint32_t r = algo->generate(status);
121 			result = result | (r << (total_size * 8));
122 			total_size += status->last_generated_size;
123 			if (EG(exception)) {
124 				return 0;
125 			}
126 		} while (total_size < sizeof(uint32_t));
127 	}
128 
129 	return result % umax;
130 }
131 
php_random_range64(const php_random_algo * algo,php_random_status * status,uint64_t umax)132 PHPAPI uint64_t php_random_range64(const php_random_algo *algo, php_random_status *status, uint64_t umax)
133 {
134 	uint64_t result, limit;
135 	size_t total_size = 0;
136 	uint32_t count = 0;
137 
138 	result = 0;
139 	total_size = 0;
140 	do {
141 		uint64_t r = algo->generate(status);
142 		result = result | (r << (total_size * 8));
143 		total_size += status->last_generated_size;
144 		if (EG(exception)) {
145 			return 0;
146 		}
147 	} while (total_size < sizeof(uint64_t));
148 
149 	/* Special case where no modulus is required */
150 	if (UNEXPECTED(umax == UINT64_MAX)) {
151 		return result;
152 	}
153 
154 	/* Increment the max so range is inclusive of max */
155 	umax++;
156 
157 	/* Powers of two are not biased */
158 	if ((umax & (umax - 1)) == 0) {
159 		return result & (umax - 1);
160 	}
161 
162 	/* Ceiling under which UINT64_MAX % max == 0 */
163 	limit = UINT64_MAX - (UINT64_MAX % umax) - 1;
164 
165 	/* Discard numbers over the limit to avoid modulo bias */
166 	while (UNEXPECTED(result > limit)) {
167 		/* If the requirements cannot be met in a cycles, return fail */
168 		if (++count > PHP_RANDOM_RANGE_ATTEMPTS) {
169 			zend_throw_error(random_ce_Random_BrokenRandomEngineError, "Failed to generate an acceptable random number in %d attempts", PHP_RANDOM_RANGE_ATTEMPTS);
170 			return 0;
171 		}
172 
173 		result = 0;
174 		total_size = 0;
175 		do {
176 			uint64_t r = algo->generate(status);
177 			result = result | (r << (total_size * 8));
178 			total_size += status->last_generated_size;
179 			if (EG(exception)) {
180 				return 0;
181 			}
182 		} while (total_size < sizeof(uint64_t));
183 	}
184 
185 	return result % umax;
186 }
187 
php_random_engine_mt19937_new(zend_class_entry * ce)188 static zend_object *php_random_engine_mt19937_new(zend_class_entry *ce)
189 {
190 	return &php_random_engine_common_init(ce, &random_engine_mt19937_object_handlers, &php_random_algo_mt19937)->std;
191 }
192 
php_random_engine_pcgoneseq128xslrr64_new(zend_class_entry * ce)193 static zend_object *php_random_engine_pcgoneseq128xslrr64_new(zend_class_entry *ce)
194 {
195 	return &php_random_engine_common_init(ce, &random_engine_pcgoneseq128xslrr64_object_handlers, &php_random_algo_pcgoneseq128xslrr64)->std;
196 }
197 
php_random_engine_xoshiro256starstar_new(zend_class_entry * ce)198 static zend_object *php_random_engine_xoshiro256starstar_new(zend_class_entry *ce)
199 {
200 	return &php_random_engine_common_init(ce, &random_engine_xoshiro256starstar_object_handlers, &php_random_algo_xoshiro256starstar)->std;
201 }
202 
php_random_engine_secure_new(zend_class_entry * ce)203 static zend_object *php_random_engine_secure_new(zend_class_entry *ce)
204 {
205 	return &php_random_engine_common_init(ce, &random_engine_secure_object_handlers, &php_random_algo_secure)->std;
206 }
207 
php_random_randomizer_new(zend_class_entry * ce)208 static zend_object *php_random_randomizer_new(zend_class_entry *ce)
209 {
210 	php_random_randomizer *randomizer = zend_object_alloc(sizeof(php_random_randomizer), ce);
211 
212 	zend_object_std_init(&randomizer->std, ce);
213 	object_properties_init(&randomizer->std, ce);
214 
215 	return &randomizer->std;
216 }
217 
randomizer_free_obj(zend_object * object)218 static void randomizer_free_obj(zend_object *object) {
219 	php_random_randomizer *randomizer = php_random_randomizer_from_obj(object);
220 
221 	if (randomizer->is_userland_algo) {
222 		php_random_status_free(randomizer->status, false);
223 	}
224 
225 	zend_object_std_dtor(&randomizer->std);
226 }
227 
php_random_status_alloc(const php_random_algo * algo,const bool persistent)228 PHPAPI php_random_status *php_random_status_alloc(const php_random_algo *algo, const bool persistent)
229 {
230 	php_random_status *status = pecalloc(1, sizeof(php_random_status), persistent);
231 
232 	status->last_generated_size = algo->generate_size;
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->last_generated_size = old_status->last_generated_size;
241 	new_status->state = memcpy(new_status->state, old_status->state, algo->state_size);
242 
243 	return new_status;
244 }
245 
php_random_status_free(php_random_status * status,const bool persistent)246 PHPAPI void php_random_status_free(php_random_status *status, const bool persistent)
247 {
248 	if (status != NULL) {
249 		pefree(status->state, persistent);
250 	}
251 
252 	pefree(status, persistent);
253 }
254 
php_random_engine_common_init(zend_class_entry * ce,zend_object_handlers * handlers,const php_random_algo * algo)255 PHPAPI php_random_engine *php_random_engine_common_init(zend_class_entry *ce, zend_object_handlers *handlers, const php_random_algo *algo)
256 {
257 	php_random_engine *engine = zend_object_alloc(sizeof(php_random_engine), ce);
258 
259 	zend_object_std_init(&engine->std, ce);
260 	object_properties_init(&engine->std, ce);
261 
262 	engine->algo = algo;
263 	engine->status = php_random_status_alloc(engine->algo, false);
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->status, 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->algo = old_engine->algo;
283 	if (old_engine->status) {
284 		new_engine->status = php_random_status_copy(old_engine->algo, old_engine->status, new_engine->status);
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(const php_random_algo * algo,php_random_status * status,zend_long min,zend_long max)293 PHPAPI zend_long php_random_range(const php_random_algo *algo, php_random_status *status, 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(algo, status, umax) + min);
299 	}
300 
301 	return (zend_long) (php_random_range32(algo, status, 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 php_random_status *php_random_default_status(void)
314 {
315 	php_random_status *status = RANDOM_G(mt19937);
316 
317 	if (!RANDOM_G(mt19937_seeded)) {
318 		((php_random_status_state_mt19937 *)status->state)->mode = MT_RAND_MT19937;
319 		php_random_mt19937_seed_default(status->state);
320 		RANDOM_G(mt19937_seeded) = true;
321 	}
322 
323 	return status;
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 (zend_long j = (len - 1); 0 <= j; j--) {
343 #else
344 	for (zend_long j = 0; j < len; j++) {
345 #endif
346 		ZSTR_VAL(str)[i++] = hexconvtab[((unsigned char *) ptr)[j] >> 4];
347 		ZSTR_VAL(str)[i++] = hexconvtab[((unsigned char *) ptr)[j] & 15];
348 	}
349 	ZSTR_VAL(str)[i] = '\0';
350 
351 	return str;
352 }
353 /* }}} */
354 
355 /* {{{ php_random_hex2bin_le */
356 /* stolen from standard/string.c */
357 PHPAPI bool php_random_hex2bin_le(zend_string *hexstr, void *dest)
358 {
359 	size_t len = hexstr->len >> 1;
360 	unsigned char *str = (unsigned char *) hexstr->val, c, l, d;
361 	unsigned char *ptr = (unsigned char *) dest;
362 	int is_letter, i = 0;
363 
364 #ifdef WORDS_BIGENDIAN
365 	/* force little endian */
366 	for (zend_long j = (len - 1); 0 <= j; j--) {
367 #else
368 	for (zend_long j = 0; j < len; j++) {
369 #endif
370 		c = str[i++];
371 		l = c & ~0x20;
372 		is_letter = ((uint32_t) ((l - 'A') ^ (l - 'F' - 1))) >> (8 * sizeof(uint32_t) - 1);
373 
374 		/* basically (c >= '0' && c <= '9') || (l >= 'A' && l <= 'F') */
375 		if (EXPECTED((((c ^ '0') - 10) >> (8 * sizeof(uint32_t) - 1)) | is_letter)) {
376 			d = (l - 0x10 - 0x27 * is_letter) << 4;
377 		} else {
378 			return false;
379 		}
380 		c = str[i++];
381 		l = c & ~0x20;
382 		is_letter = ((uint32_t) ((l - 'A') ^ (l - 'F' - 1))) >> (8 * sizeof(uint32_t) - 1);
383 		if (EXPECTED((((c ^ '0') - 10) >> (8 * sizeof(uint32_t) - 1)) | is_letter)) {
384 			d |= l - 0x10 - 0x27 * is_letter;
385 		} else {
386 			return false;
387 		}
388 		ptr[j] = d;
389 	}
390 	return true;
391 }
392 /* }}} */
393 
394 /* {{{ php_combined_lcg */
395 PHPAPI double php_combined_lcg(void)
396 {
397 	php_random_status *status = RANDOM_G(combined_lcg);
398 
399 	if (!RANDOM_G(combined_lcg_seeded)) {
400 		php_random_combinedlcg_seed_default(status->state);
401 		RANDOM_G(combined_lcg_seeded) = true;
402 	}
403 
404 	return php_random_algo_combinedlcg.generate(status) * 4.656613e-10;
405 }
406 /* }}} */
407 
408 /* {{{ php_mt_srand */
409 PHPAPI void php_mt_srand(uint32_t seed)
410 {
411 	/* Seed the generator with a simple uint32 */
412 	php_random_algo_mt19937.seed(php_random_default_status(), (zend_long) seed);
413 }
414 /* }}} */
415 
416 /* {{{ php_mt_rand */
417 PHPAPI uint32_t php_mt_rand(void)
418 {
419 	return (uint32_t) php_random_algo_mt19937.generate(php_random_default_status());
420 }
421 /* }}} */
422 
423 /* {{{ php_mt_rand_range */
424 PHPAPI zend_long php_mt_rand_range(zend_long min, zend_long max)
425 {
426 	return php_random_algo_mt19937.range(php_random_default_status(), min, max);
427 }
428 /* }}} */
429 
430 /* {{{ php_mt_rand_common
431  * rand() allows min > max, mt_rand does not */
432 PHPAPI zend_long php_mt_rand_common(zend_long min, zend_long max)
433 {
434 	php_random_status *status = php_random_default_status();
435 	php_random_status_state_mt19937 *s = status->state;
436 
437 	if (s->mode == MT_RAND_MT19937) {
438 		return php_mt_rand_range(min, max);
439 	}
440 
441 	uint64_t r = php_random_algo_mt19937.generate(php_random_default_status()) >> 1;
442 
443 	/* This is an inlined version of the RAND_RANGE_BADSCALING macro that does not invoke UB when encountering
444 	 * (max - min) > ZEND_LONG_MAX.
445 	 */
446 	zend_ulong offset = (double) ( (double) max - min + 1.0) * (r / (PHP_MT_RAND_MAX + 1.0));
447 
448 	return (zend_long) (offset + min);
449 }
450 /* }}} */
451 
452 /* {{{ php_srand */
453 PHPAPI void php_srand(zend_long seed)
454 {
455 	php_mt_srand((uint32_t) seed);
456 }
457 /* }}} */
458 
459 /* {{{ php_rand */
460 PHPAPI zend_long php_rand(void)
461 {
462 	return php_mt_rand();
463 }
464 /* }}} */
465 
466 /* {{{ Returns a value from the combined linear congruential generator */
467 PHP_FUNCTION(lcg_value)
468 {
469 	ZEND_PARSE_PARAMETERS_NONE();
470 
471 	RETURN_DOUBLE(php_combined_lcg());
472 }
473 /* }}} */
474 
475 /* {{{ Seeds Mersenne Twister random number generator */
476 PHP_FUNCTION(mt_srand)
477 {
478 	zend_long seed = 0;
479 	bool seed_is_null = true;
480 	zend_long mode = MT_RAND_MT19937;
481 	php_random_status *status = RANDOM_G(mt19937);
482 	php_random_status_state_mt19937 *state = status->state;
483 
484 	ZEND_PARSE_PARAMETERS_START(0, 2)
485 		Z_PARAM_OPTIONAL
486 		Z_PARAM_LONG_OR_NULL(seed, seed_is_null)
487 		Z_PARAM_LONG(mode)
488 	ZEND_PARSE_PARAMETERS_END();
489 
490 	switch (mode) {
491 	case MT_RAND_PHP:
492 		state->mode = MT_RAND_PHP;
493 		zend_error(E_DEPRECATED, "The MT_RAND_PHP variant of Mt19937 is deprecated");
494 		break;
495 	default:
496 		state->mode = MT_RAND_MT19937;
497 	}
498 
499 	if (seed_is_null) {
500 		php_random_mt19937_seed_default(status->state);
501 	} else {
502 		php_random_algo_mt19937.seed(status, (uint64_t) seed);
503 	}
504 	RANDOM_G(mt19937_seeded) = true;
505 }
506 /* }}} */
507 
508 /* {{{ Returns a random number from Mersenne Twister */
509 PHP_FUNCTION(mt_rand)
510 {
511 	zend_long min, max;
512 	int argc = ZEND_NUM_ARGS();
513 
514 	if (argc == 0) {
515 		/* genrand_int31 in mt19937ar.c performs a right shift */
516 		RETURN_LONG(php_mt_rand() >> 1);
517 	}
518 
519 	ZEND_PARSE_PARAMETERS_START(2, 2)
520 		Z_PARAM_LONG(min)
521 		Z_PARAM_LONG(max)
522 	ZEND_PARSE_PARAMETERS_END();
523 
524 	if (UNEXPECTED(max < min)) {
525 		zend_argument_value_error(2, "must be greater than or equal to argument #1 ($min)");
526 		RETURN_THROWS();
527 	}
528 
529 	RETURN_LONG(php_mt_rand_common(min, max));
530 }
531 /* }}} */
532 
533 /* {{{ Returns the maximum value a random number from Mersenne Twister can have */
534 PHP_FUNCTION(mt_getrandmax)
535 {
536 	ZEND_PARSE_PARAMETERS_NONE();
537 
538 	/*
539 	 * Melo: it could be 2^^32 but we only use 2^^31 to maintain
540 	 * compatibility with the previous php_rand
541 	 */
542 	RETURN_LONG(PHP_MT_RAND_MAX); /* 2^^31 */
543 }
544 /* }}} */
545 
546 /* {{{ Returns a random number from Mersenne Twister */
547 PHP_FUNCTION(rand)
548 {
549 	zend_long min, max;
550 	int argc = ZEND_NUM_ARGS();
551 
552 	if (argc == 0) {
553 		/* genrand_int31 in mt19937ar.c performs a right shift */
554 		RETURN_LONG(php_mt_rand() >> 1);
555 	}
556 
557 	ZEND_PARSE_PARAMETERS_START(2, 2)
558 		Z_PARAM_LONG(min)
559 		Z_PARAM_LONG(max)
560 	ZEND_PARSE_PARAMETERS_END();
561 
562 	if (max < min) {
563 		RETURN_LONG(php_mt_rand_common(max, min));
564 	}
565 
566 	RETURN_LONG(php_mt_rand_common(min, max));
567 }
568 /* }}} */
569 
570 /* {{{ Return an arbitrary length of pseudo-random bytes as binary string */
571 PHP_FUNCTION(random_bytes)
572 {
573 	zend_long size;
574 	zend_string *bytes;
575 
576 	ZEND_PARSE_PARAMETERS_START(1, 1)
577 		Z_PARAM_LONG(size)
578 	ZEND_PARSE_PARAMETERS_END();
579 
580 	if (size < 1) {
581 		zend_argument_value_error(1, "must be greater than 0");
582 		RETURN_THROWS();
583 	}
584 
585 	bytes = zend_string_alloc(size, 0);
586 
587 	if (php_random_bytes_throw(ZSTR_VAL(bytes), size) == FAILURE) {
588 		zend_string_release_ex(bytes, 0);
589 		RETURN_THROWS();
590 	}
591 
592 	ZSTR_VAL(bytes)[size] = '\0';
593 
594 	RETURN_STR(bytes);
595 }
596 /* }}} */
597 
598 /* {{{ Return an arbitrary pseudo-random integer */
599 PHP_FUNCTION(random_int)
600 {
601 	zend_long min, max, result;
602 
603 	ZEND_PARSE_PARAMETERS_START(2, 2)
604 		Z_PARAM_LONG(min)
605 		Z_PARAM_LONG(max)
606 	ZEND_PARSE_PARAMETERS_END();
607 
608 	if (min > max) {
609 		zend_argument_value_error(1, "must be less than or equal to argument #2 ($max)");
610 		RETURN_THROWS();
611 	}
612 
613 	if (php_random_int_throw(min, max, &result) == FAILURE) {
614 		RETURN_THROWS();
615 	}
616 
617 	RETURN_LONG(result);
618 }
619 /* }}} */
620 
621 /* {{{ PHP_GINIT_FUNCTION */
622 static PHP_GINIT_FUNCTION(random)
623 {
624 	random_globals->random_fd = -1;
625 
626 	random_globals->combined_lcg = php_random_status_alloc(&php_random_algo_combinedlcg, true);
627 	random_globals->combined_lcg_seeded = false;
628 
629 	random_globals->mt19937 = php_random_status_alloc(&php_random_algo_mt19937, true);
630 	random_globals->mt19937_seeded = false;
631 }
632 /* }}} */
633 
634 /* {{{ PHP_GSHUTDOWN_FUNCTION */
635 static PHP_GSHUTDOWN_FUNCTION(random)
636 {
637 	if (random_globals->random_fd >= 0) {
638 		close(random_globals->random_fd);
639 		random_globals->random_fd = -1;
640 	}
641 
642 	php_random_status_free(random_globals->combined_lcg, true);
643 	random_globals->combined_lcg = NULL;
644 
645 	php_random_status_free(random_globals->mt19937, true);
646 	random_globals->mt19937 = NULL;
647 }
648 /* }}} */
649 
650 /* {{{ PHP_MINIT_FUNCTION */
651 PHP_MINIT_FUNCTION(random)
652 {
653 	/* Random\Engine */
654 	random_ce_Random_Engine = register_class_Random_Engine();
655 
656 	/* Random\CryptoSafeEngine */
657 	random_ce_Random_CryptoSafeEngine = register_class_Random_CryptoSafeEngine(random_ce_Random_Engine);
658 
659 	/* Random\RandomError */
660 	random_ce_Random_RandomError = register_class_Random_RandomError(zend_ce_error);
661 
662 	/* Random\BrokenRandomEngineError */
663 	random_ce_Random_BrokenRandomEngineError = register_class_Random_BrokenRandomEngineError(random_ce_Random_RandomError);
664 
665 	/* Random\RandomException */
666 	random_ce_Random_RandomException = register_class_Random_RandomException(zend_ce_exception);
667 
668 	/* Random\Engine\Mt19937 */
669 	random_ce_Random_Engine_Mt19937 = register_class_Random_Engine_Mt19937(random_ce_Random_Engine);
670 	random_ce_Random_Engine_Mt19937->create_object = php_random_engine_mt19937_new;
671 	memcpy(&random_engine_mt19937_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
672 	random_engine_mt19937_object_handlers.offset = XtOffsetOf(php_random_engine, std);
673 	random_engine_mt19937_object_handlers.free_obj = php_random_engine_common_free_object;
674 	random_engine_mt19937_object_handlers.clone_obj = php_random_engine_common_clone_object;
675 
676 	/* Random\Engine\PcgOnseq128XslRr64 */
677 	random_ce_Random_Engine_PcgOneseq128XslRr64 = register_class_Random_Engine_PcgOneseq128XslRr64(random_ce_Random_Engine);
678 	random_ce_Random_Engine_PcgOneseq128XslRr64->create_object = php_random_engine_pcgoneseq128xslrr64_new;
679 	memcpy(&random_engine_pcgoneseq128xslrr64_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
680 	random_engine_pcgoneseq128xslrr64_object_handlers.offset = XtOffsetOf(php_random_engine, std);
681 	random_engine_pcgoneseq128xslrr64_object_handlers.free_obj = php_random_engine_common_free_object;
682 	random_engine_pcgoneseq128xslrr64_object_handlers.clone_obj = php_random_engine_common_clone_object;
683 
684 	/* Random\Engine\Xoshiro256StarStar */
685 	random_ce_Random_Engine_Xoshiro256StarStar = register_class_Random_Engine_Xoshiro256StarStar(random_ce_Random_Engine);
686 	random_ce_Random_Engine_Xoshiro256StarStar->create_object = php_random_engine_xoshiro256starstar_new;
687 	memcpy(&random_engine_xoshiro256starstar_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
688 	random_engine_xoshiro256starstar_object_handlers.offset = XtOffsetOf(php_random_engine, std);
689 	random_engine_xoshiro256starstar_object_handlers.free_obj = php_random_engine_common_free_object;
690 	random_engine_xoshiro256starstar_object_handlers.clone_obj = php_random_engine_common_clone_object;
691 
692 	/* Random\Engine\Secure */
693 	random_ce_Random_Engine_Secure = register_class_Random_Engine_Secure(random_ce_Random_CryptoSafeEngine);
694 	random_ce_Random_Engine_Secure->create_object = php_random_engine_secure_new;
695 	memcpy(&random_engine_secure_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
696 	random_engine_secure_object_handlers.offset = XtOffsetOf(php_random_engine, std);
697 	random_engine_secure_object_handlers.free_obj = php_random_engine_common_free_object;
698 	random_engine_secure_object_handlers.clone_obj = NULL;
699 
700 	/* Random\Randomizer */
701 	random_ce_Random_Randomizer = register_class_Random_Randomizer();
702 	random_ce_Random_Randomizer->create_object = php_random_randomizer_new;
703 	random_ce_Random_Randomizer->default_object_handlers = &random_randomizer_object_handlers;
704 	memcpy(&random_randomizer_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
705 	random_randomizer_object_handlers.offset = XtOffsetOf(php_random_randomizer, std);
706 	random_randomizer_object_handlers.free_obj = randomizer_free_obj;
707 	random_randomizer_object_handlers.clone_obj = NULL;
708 
709 	/* Random\IntervalBoundary */
710 	random_ce_Random_IntervalBoundary = register_class_Random_IntervalBoundary();
711 
712 	register_random_symbols(module_number);
713 
714 	return SUCCESS;
715 }
716 /* }}} */
717 
718 /* {{{ PHP_RINIT_FUNCTION */
719 PHP_RINIT_FUNCTION(random)
720 {
721 	RANDOM_G(combined_lcg_seeded) = false;
722 	RANDOM_G(mt19937_seeded) = false;
723 
724 	return SUCCESS;
725 }
726 /* }}} */
727 
728 /* {{{ random_module_entry */
729 zend_module_entry random_module_entry = {
730 	STANDARD_MODULE_HEADER,
731 	"random",					/* Extension name */
732 	ext_functions,				/* zend_function_entry */
733 	PHP_MINIT(random),			/* PHP_MINIT - Module initialization */
734 	NULL,						/* PHP_MSHUTDOWN - Module shutdown */
735 	PHP_RINIT(random),			/* PHP_RINIT - Request initialization */
736 	NULL,						/* PHP_RSHUTDOWN - Request shutdown */
737 	NULL,						/* PHP_MINFO - Module info */
738 	PHP_VERSION,				/* Version */
739 	PHP_MODULE_GLOBALS(random),	/* ZTS Module globals */
740 	PHP_GINIT(random),			/* PHP_GINIT - Global initialization */
741 	PHP_GSHUTDOWN(random),		/* PHP_GSHUTDOWN - Global shutdown */
742 	NULL,						/* Post deactivate */
743 	STANDARD_MODULE_PROPERTIES_EX
744 };
745 /* }}} */
746