From 2e6b7421301e78be7d05bdf45d395a755b6e6bda Mon Sep 17 00:00:00 2001 From: Go Kudo Date: Tue, 19 Jul 2022 12:10:55 +0900 Subject: [PATCH] Implement Random Extension https://wiki.php.net/rfc/rng_extension https://wiki.php.net/rfc/random_extension_improvement --- EXTENSIONS | 6 + NEWS | 5 + Zend/Optimizer/zend_func_infos.h | 2 +- ext/dom/php_dom.c | 2 +- ext/gmp/gmp.c | 3 +- ext/random/CREDITS | 2 + ext/random/config.m4 | 25 + ext/random/config.w32 | 4 + ext/random/engine_combinedlcg.c | 128 +++ ext/random/engine_mt19937.c | 400 ++++++++ ext/random/engine_pcgoneseq128xslrr64.c | 191 ++++ ext/random/engine_secure.c | 58 ++ ext/random/engine_user.c | 70 ++ ext/random/engine_xoshiro256starstar.c | 236 +++++ ext/random/php_random.h | 323 +++++++ ext/random/random.c | 911 ++++++++++++++++++ ext/random/random.stub.php | 137 +++ ext/random/random_arginfo.h | 304 ++++++ ext/random/randomizer.c | 285 ++++++ .../tests/01_functions}/bug46587.phpt | 0 .../tests/01_functions}/bug75170.phpt | 0 .../tests/01_functions}/bug75514.phpt | 0 .../tests/01_functions}/lcg_value_basic.phpt | 0 .../01_functions}/mt_getrandmax_basic.phpt | 0 .../tests/01_functions}/mt_rand_basic.phpt | 0 .../tests/01_functions}/mt_rand_value.phpt | 0 .../tests/01_functions}/mt_srand_basic.phpt | 0 .../tests/01_functions}/rand_basic.phpt | 0 .../01_functions}/rand_inverted_order.phpt | 0 .../tests/01_functions}/random_bytes.phpt | 0 .../01_functions}/random_bytes_error.phpt | 0 .../tests/01_functions}/random_int.phpt | 0 .../tests/01_functions}/random_int_error.phpt | 0 .../tests/01_functions}/reflection.phpt | 0 .../tests/01_functions}/srand_basic.phpt | 0 .../tests/02_engine/all_serialize_error.phpt | 77 ++ .../tests/02_engine/all_serialize_native.phpt | 27 + .../tests/02_engine/all_serialize_user.phpt | 48 + ext/random/tests/02_engine/mt19937_error.phpt | 14 + .../tests/02_engine/mt19937_serialize.phpt | 10 + ext/random/tests/02_engine/mt19937_value.phpt | 15 + .../02_engine/pcgoneseq128xslrr64_seed.phpt | 43 + .../pcgoneseq128xslrr64_serialize.phpt | 10 + .../02_engine/pcgoneseq128xslrr64_value.phpt | 17 + .../tests/02_engine/user_compatibility.phpt | 63 ++ .../02_engine/user_xoshiro128plusplus.phpt | 61 ++ .../02_engine/xoshiro256starstar_seed.phpt | 47 + .../xoshiro256starstar_serialize.phpt | 10 + .../02_engine/xoshiro256starstar_value.phpt | 18 + ext/random/tests/03_randomizer/basic.phpt | 70 ++ .../tests/03_randomizer/compatibility_mt.phpt | 24 + .../03_randomizer/compatibility_user.phpt | 86 ++ ext/random/tests/03_randomizer/get_bytes.phpt | 40 + .../tests/03_randomizer/pick_array_keys.phpt | 48 + .../03_randomizer/pick_array_keys_error.phpt | 52 + ext/random/tests/03_randomizer/readonly.phpt | 43 + ext/random/tests/03_randomizer/serialize.phpt | 57 ++ .../tests/03_randomizer/user_unsafe.phpt | 83 ++ ext/reflection/php_reflection.c | 2 +- ext/session/session.c | 3 +- ext/soap/php_http.c | 2 +- ext/spl/php_spl.c | 2 +- ext/standard/array.c | 140 +-- ext/standard/basic_functions.c | 12 - ext/standard/basic_functions.h | 13 +- ext/standard/basic_functions.stub.php | 27 - ext/standard/basic_functions_arginfo.h | 51 +- ext/standard/config.m4 | 6 +- ext/standard/config.w32 | 6 +- ext/standard/crypt.c | 2 +- ext/standard/lcg.c | 120 --- ext/standard/mt_rand.c | 356 ------- ext/standard/password.c | 3 +- ext/standard/php_array.h | 5 + ext/standard/php_lcg.h | 39 +- ext/standard/php_mt_rand.h | 41 +- ext/standard/php_rand.h | 73 +- ext/standard/php_random.h | 51 +- ext/standard/php_standard.h | 2 - ext/standard/php_string.h | 8 + ext/standard/rand.c | 65 -- ext/standard/random.c | 308 ------ ext/standard/string.c | 24 +- ext/standard/uniqid.c | 3 +- main/internal_functions_win32.c | 2 +- main/reentrancy.c | 2 +- 86 files changed, 4193 insertions(+), 1230 deletions(-) create mode 100644 ext/random/CREDITS create mode 100644 ext/random/config.m4 create mode 100644 ext/random/config.w32 create mode 100644 ext/random/engine_combinedlcg.c create mode 100644 ext/random/engine_mt19937.c create mode 100644 ext/random/engine_pcgoneseq128xslrr64.c create mode 100644 ext/random/engine_secure.c create mode 100644 ext/random/engine_user.c create mode 100644 ext/random/engine_xoshiro256starstar.c create mode 100644 ext/random/php_random.h create mode 100644 ext/random/random.c create mode 100644 ext/random/random.stub.php create mode 100644 ext/random/random_arginfo.h create mode 100644 ext/random/randomizer.c rename ext/{standard/tests/general_functions => random/tests/01_functions}/bug46587.phpt (100%) rename ext/{standard/tests/math => random/tests/01_functions}/bug75170.phpt (100%) rename ext/{standard/tests/math => random/tests/01_functions}/bug75514.phpt (100%) rename ext/{standard/tests/math => random/tests/01_functions}/lcg_value_basic.phpt (100%) rename ext/{standard/tests/math => random/tests/01_functions}/mt_getrandmax_basic.phpt (100%) rename ext/{standard/tests/math => random/tests/01_functions}/mt_rand_basic.phpt (100%) rename ext/{standard/tests/math => random/tests/01_functions}/mt_rand_value.phpt (100%) rename ext/{standard/tests/math => random/tests/01_functions}/mt_srand_basic.phpt (100%) rename ext/{standard/tests/math => random/tests/01_functions}/rand_basic.phpt (100%) rename ext/{standard/tests/math => random/tests/01_functions}/rand_inverted_order.phpt (100%) rename ext/{standard/tests/random => random/tests/01_functions}/random_bytes.phpt (100%) rename ext/{standard/tests/random => random/tests/01_functions}/random_bytes_error.phpt (100%) rename ext/{standard/tests/random => random/tests/01_functions}/random_int.phpt (100%) rename ext/{standard/tests/random => random/tests/01_functions}/random_int_error.phpt (100%) rename ext/{standard/tests/random => random/tests/01_functions}/reflection.phpt (100%) rename ext/{standard/tests/math => random/tests/01_functions}/srand_basic.phpt (100%) create mode 100644 ext/random/tests/02_engine/all_serialize_error.phpt create mode 100644 ext/random/tests/02_engine/all_serialize_native.phpt create mode 100644 ext/random/tests/02_engine/all_serialize_user.phpt create mode 100644 ext/random/tests/02_engine/mt19937_error.phpt create mode 100644 ext/random/tests/02_engine/mt19937_serialize.phpt create mode 100644 ext/random/tests/02_engine/mt19937_value.phpt create mode 100644 ext/random/tests/02_engine/pcgoneseq128xslrr64_seed.phpt create mode 100644 ext/random/tests/02_engine/pcgoneseq128xslrr64_serialize.phpt create mode 100644 ext/random/tests/02_engine/pcgoneseq128xslrr64_value.phpt create mode 100644 ext/random/tests/02_engine/user_compatibility.phpt create mode 100644 ext/random/tests/02_engine/user_xoshiro128plusplus.phpt create mode 100644 ext/random/tests/02_engine/xoshiro256starstar_seed.phpt create mode 100644 ext/random/tests/02_engine/xoshiro256starstar_serialize.phpt create mode 100644 ext/random/tests/02_engine/xoshiro256starstar_value.phpt create mode 100644 ext/random/tests/03_randomizer/basic.phpt create mode 100644 ext/random/tests/03_randomizer/compatibility_mt.phpt create mode 100644 ext/random/tests/03_randomizer/compatibility_user.phpt create mode 100644 ext/random/tests/03_randomizer/get_bytes.phpt create mode 100644 ext/random/tests/03_randomizer/pick_array_keys.phpt create mode 100644 ext/random/tests/03_randomizer/pick_array_keys_error.phpt create mode 100644 ext/random/tests/03_randomizer/readonly.phpt create mode 100644 ext/random/tests/03_randomizer/serialize.phpt create mode 100644 ext/random/tests/03_randomizer/user_unsafe.phpt delete mode 100644 ext/standard/lcg.c delete mode 100644 ext/standard/mt_rand.c delete mode 100644 ext/standard/rand.c delete mode 100644 ext/standard/random.c diff --git a/EXTENSIONS b/EXTENSIONS index 25cf9fbd6a981..0040553a73ea5 100644 --- a/EXTENSIONS +++ b/EXTENSIONS @@ -425,6 +425,12 @@ MAINTENANCE: Unknown STATUS: Working SINCE: 4.0.2 ------------------------------------------------------------------------------- +EXTENSION: random +PRIMARY MAINTAINER Go Kudo (2022 - 2022) + Tim Düsterhus (2022 - 2022) +MAINTENANCE: Maintained +STATUS: Working +------------------------------------------------------------------------------- EXTENSION: readline PRIMARY MAINTAINER: Unknown MAINTENANCE: Unknown diff --git a/NEWS b/NEWS index dc9776a280b6c..156ad74fdb0fd 100644 --- a/NEWS +++ b/NEWS @@ -43,6 +43,11 @@ PHP NEWS - PDO_Firebird: . Fixed bug GH-8576 (Bad interpretation of length when char is UTF-8). (cmb) +- Random: + . Add new random extension. (Go Kudo), This extension organizes and + consolidates existing implementations related to random number generators. + New, higher quality RNGs are available and scope issues are eliminated. (Go Kudo) + - Standard: . Fixed empty array returned by str_split on empty input. (Michael Vorisek) . Added ini_parse_quantity function to convert ini quantities shorthand diff --git a/Zend/Optimizer/zend_func_infos.h b/Zend/Optimizer/zend_func_infos.h index 1a4b0f9c5a71f..c967e1b4e1a7f 100644 --- a/Zend/Optimizer/zend_func_infos.h +++ b/Zend/Optimizer/zend_func_infos.h @@ -416,6 +416,7 @@ static const func_info_t func_infos[] = { F1("posix_getrlimit", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_LONG|MAY_BE_ARRAY_OF_STRING|MAY_BE_FALSE), #endif F1("pspell_suggest", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_STRING|MAY_BE_FALSE), + F1("random_bytes", MAY_BE_STRING), #if defined(HAVE_HISTORY_LIST) F1("readline_list_history", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_STRING), #endif @@ -651,7 +652,6 @@ static const func_info_t func_infos[] = { #endif F1("quoted_printable_decode", MAY_BE_STRING), F1("quoted_printable_encode", MAY_BE_STRING), - F1("random_bytes", MAY_BE_STRING), F1("soundex", MAY_BE_STRING), F1("stream_context_create", MAY_BE_RESOURCE), F1("stream_context_get_params", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_ANY), diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index db2d481122dd1..18b0bce90d3b8 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -22,7 +22,7 @@ #include "php.h" #if defined(HAVE_LIBXML) && defined(HAVE_DOM) -#include "ext/standard/php_rand.h" +#include "ext/random/php_random.h" #include "php_dom.h" #include "php_dom_arginfo.h" #include "dom_properties.h" diff --git a/ext/gmp/gmp.c b/ext/gmp/gmp.c index 39d0da5d154af..fff4e9293d18f 100644 --- a/ext/gmp/gmp.c +++ b/ext/gmp/gmp.c @@ -30,8 +30,7 @@ #include /* Needed for gmp_random() */ -#include "ext/standard/php_rand.h" -#include "ext/standard/php_lcg.h" +#include "ext/random/php_random.h" #define GMP_ROUND_ZERO 0 #define GMP_ROUND_PLUSINF 1 diff --git a/ext/random/CREDITS b/ext/random/CREDITS new file mode 100644 index 0000000000000..c647fb7d225b3 --- /dev/null +++ b/ext/random/CREDITS @@ -0,0 +1,2 @@ +random +Go Kudo, Tim Düsterhus, Guilliam Xavier, Christoph M. Becker, Jakub Zelenka, Bob Weinand, Máté Kocsis, and Original RNG implementators diff --git a/ext/random/config.m4 b/ext/random/config.m4 new file mode 100644 index 0000000000000..7bd432c9110c2 --- /dev/null +++ b/ext/random/config.m4 @@ -0,0 +1,25 @@ +dnl +dnl Check for arc4random on BSD systems +dnl +AC_CHECK_DECLS([arc4random_buf]) + +dnl +dnl Check for CCRandomGenerateBytes +dnl header absent in previous macOs releases +dnl +AC_CHECK_HEADERS([CommonCrypto/CommonRandom.h]) + +dnl +dnl Setup extension +dnl +PHP_NEW_EXTENSION(random, + random.c \ + engine_combinedlcg.c \ + engine_mt19937.c \ + engine_pcgoneseq128xslrr64.c \ + engine_xoshiro256starstar.c \ + engine_secure.c \ + engine_user.c \ + randomizer.c, + no,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1) +PHP_INSTALL_HEADERS([ext/random], [php_random.h]) diff --git a/ext/random/config.w32 b/ext/random/config.w32 new file mode 100644 index 0000000000000..bfbd153c1680d --- /dev/null +++ b/ext/random/config.w32 @@ -0,0 +1,4 @@ +EXTENSION("random", "random.c", false /* never shared */, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); +PHP_RANDOM="yes"; +ADD_SOURCES(configure_module_dirname, "engine_combinedlcg.c engine_mt19937.c engine_pcgoneseq128xslrr64.c engine_xoshiro256starstar.c engine_secure.c engine_user.c randomizer.c", "random"); +PHP_INSTALL_HEADERS("ext/random", "php_random.h"); diff --git a/ext/random/engine_combinedlcg.c b/ext/random/engine_combinedlcg.c new file mode 100644 index 0000000000000..35c997da76340 --- /dev/null +++ b/ext/random/engine_combinedlcg.c @@ -0,0 +1,128 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Sascha Schumann | + | Go Kudo | + +----------------------------------------------------------------------+ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "php.h" +#include "php_random.h" + +#include "ext/spl/spl_exceptions.h" +#include "Zend/zend_exceptions.h" + +/* + * combinedLCG() returns a pseudo random number in the range of (0, 1). + * The function combines two CGs with periods of + * 2^31 - 85 and 2^31 - 249. The period of this function + * is equal to the product of both primes. + */ +#define MODMULT(a, b, c, m, s) q = s / a; s = b * (s - a * q) - c * q; if (s < 0) s += m + +static void seed(php_random_status *status, uint64_t seed) +{ + php_random_status_state_combinedlcg *s = status->state; + + s->state[0] = seed & 0xffffffffU; + s->state[1] = seed >> 32; +} + +static uint64_t generate(php_random_status *status) +{ + php_random_status_state_combinedlcg *s = status->state; + int32_t q, z; + + MODMULT(53668, 40014, 12211, 2147483563L, s->state[0]); + MODMULT(52774, 40692, 3791, 2147483399L, s->state[1]); + + z = s->state[0] - s->state[1]; + if (z < 1) { + z += 2147483562; + } + + return (uint64_t) z; +} + +static zend_long range(php_random_status *status, zend_long min, zend_long max) +{ + return php_random_range(&php_random_algo_combinedlcg, status, min, max); +} + +static bool serialize(php_random_status *status, HashTable *data) +{ + php_random_status_state_combinedlcg *s = status->state; + zval t; + + for (uint32_t i = 0; i < 2; i++) { + ZVAL_STR(&t, php_random_bin2hex_le(&s->state[i], sizeof(uint32_t))); + zend_hash_next_index_insert(data, &t); + } + + return true; +} + +static bool unserialize(php_random_status *status, HashTable *data) +{ + php_random_status_state_combinedlcg *s = status->state; + zval *t; + + for (uint32_t i = 0; i < 2; i++) { + t = zend_hash_index_find(data, i); + if (!t || Z_TYPE_P(t) != IS_STRING || Z_STRLEN_P(t) != (2 * sizeof(uint32_t))) { + return false; + } + if (!php_random_hex2bin_le(Z_STR_P(t), &s->state[i])) { + return false; + } + } + + return true; +} + +const php_random_algo php_random_algo_combinedlcg = { + sizeof(uint32_t), + sizeof(php_random_status_state_combinedlcg), + seed, + generate, + range, + serialize, + unserialize +}; + +/* {{{ php_random_combinedlcg_seed_default */ +PHPAPI void php_random_combinedlcg_seed_default(php_random_status_state_combinedlcg *state) +{ + struct timeval tv; + + if (gettimeofday(&tv, NULL) == 0) { + state->state[0] = tv.tv_usec ^ (tv.tv_usec << 11); + } else { + state->state[0] = 1; + } + +#ifdef ZTS + state->state[1] = (zend_long) tsrm_thread_id(); +#else + state->state[1] = (zend_long) getpid(); +#endif + + /* Add entropy to s2 by calling gettimeofday() again */ + if (gettimeofday(&tv, NULL) == 0) { + state->state[1] ^= (tv.tv_usec << 11); + } +} +/* }}} */ diff --git a/ext/random/engine_mt19937.c b/ext/random/engine_mt19937.c new file mode 100644 index 0000000000000..6e438fea5fda1 --- /dev/null +++ b/ext/random/engine_mt19937.c @@ -0,0 +1,400 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Rasmus Lerdorf | + | Zeev Suraski | + | Pedro Melo | + | Sterling Hughes | + | Go Kudo | + | | + | Based on code from: Richard J. Wagner | + | Makoto Matsumoto | + | Takuji Nishimura | + | Shawn Cokus | + +----------------------------------------------------------------------+ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "php.h" +#include "php_random.h" + +#include "ext/spl/spl_exceptions.h" +#include "Zend/zend_exceptions.h" + +/* + The following mt19937 algorithms are based on a C++ class MTRand by + Richard J. Wagner. For more information see the web page at + http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/VERSIONS/C-LANG/MersenneTwister.h + + Mersenne Twister random number generator -- a C++ class MTRand + Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus + Richard J. Wagner v1.0 15 May 2003 rjwagner@writeme.com + + The Mersenne Twister is an algorithm for generating random numbers. It + was designed with consideration of the flaws in various other generators. + The period, 2^19937-1, and the order of equidistribution, 623 dimensions, + are far greater. The generator is also fast; it avoids multiplication and + division, and it benefits from caches and pipelines. For more information + see the inventors' web page at http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html + + Reference + M. Matsumoto and T. Nishimura, "Mersenne Twister: A 623-Dimensionally + Equidistributed Uniform Pseudo-Random Number Generator", ACM Transactions on + Modeling and Computer Simulation, Vol. 8, No. 1, January 1998, pp 3-30. + + Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, + Copyright (C) 2000 - 2003, Richard J. Wagner + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. The names of its contributors may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#define N MT_N /* length of state vector */ +#define M (397) /* a period parameter */ +#define hiBit(u) ((u) & 0x80000000U) /* mask all but highest bit of u */ +#define loBit(u) ((u) & 0x00000001U) /* mask all but lowest bit of u */ +#define loBits(u) ((u) & 0x7FFFFFFFU) /* mask the highest bit of u */ +#define mixBits(u, v) (hiBit(u) | loBits(v)) /* move hi bit of u to hi bit of v */ + +#define twist(m,u,v) (m ^ (mixBits(u,v) >> 1) ^ ((uint32_t)(-(int32_t)(loBit(v))) & 0x9908b0dfU)) +#define twist_php(m,u,v) (m ^ (mixBits(u,v) >> 1) ^ ((uint32_t)(-(int32_t)(loBit(u))) & 0x9908b0dfU)) + +static inline void mt19937_reload(php_random_status_state_mt19937 *state) +{ + uint32_t *p = state->state; + + if (state->mode == MT_RAND_MT19937) { + for (uint32_t i = N - M; i--; ++p) { + *p = twist(p[M], p[0], p[1]); + } + for (uint32_t i = M; --i; ++p) { + *p = twist(p[M-N], p[0], p[1]); + } + *p = twist(p[M-N], p[0], state->state[0]); + } else { + for (uint32_t i = N - M; i--; ++p) { + *p = twist_php(p[M], p[0], p[1]); + } + for (uint32_t i = M; --i; ++p) { + *p = twist_php(p[M-N], p[0], p[1]); + } + *p = twist_php(p[M-N], p[0], state->state[0]); + } + + state->count = 0; +} + +static inline void mt19937_seed_state(php_random_status_state_mt19937 *state, uint64_t seed) +{ + uint32_t i, prev_state; + + /* Initialize generator state with seed + See Knuth TAOCP Vol 2, 3rd Ed, p.106 for multiplier. + In previous versions, most significant bits (MSBs) of the seed affect + only MSBs of the state array. Modified 9 Jan 2002 by Makoto Matsumoto. */ + state->state[0] = seed & 0xffffffffU; + for (i = 1; i < MT_N; i++) { + prev_state = state->state[i - 1]; + state->state[i] = (1812433253U * (prev_state ^ (prev_state >> 30)) + i) & 0xffffffffU; + } + state->count = i; + + mt19937_reload(state); +} + +static void seed(php_random_status *status, uint64_t seed) +{ + mt19937_seed_state(status->state, seed); +} + +static uint64_t generate(php_random_status *status) +{ + php_random_status_state_mt19937 *s = status->state; + uint32_t s1; + + if (s->count >= MT_N) { + mt19937_reload(s); + } + + s1 = s->state[s->count++]; + s1 ^= (s1 >> 11); + s1 ^= (s1 << 7) & 0x9d2c5680U; + s1 ^= (s1 << 15) & 0xefc60000U; + + return (uint64_t) (s1 ^ (s1 >> 18)); +} + +static zend_long range(php_random_status *status, zend_long min, zend_long max) +{ + php_random_status_state_mt19937 *s = status->state; + + if (s->mode == MT_RAND_MT19937) { + return php_random_range(&php_random_algo_mt19937, status, min, max); + } + + uint64_t r = php_random_algo_mt19937.generate(status) >> 1; + /* Legacy mode deliberately not inside php_mt_rand_range() + * to prevent other functions being affected */ + RAND_RANGE_BADSCALING(r, min, max, PHP_MT_RAND_MAX); + return (zend_long) r; +} + +static bool serialize(php_random_status *status, HashTable *data) +{ + php_random_status_state_mt19937 *s = status->state; + zval t; + + for (uint32_t i = 0; i < MT_N; i++) { + ZVAL_STR(&t, php_random_bin2hex_le(&s->state[i], sizeof(uint32_t))); + zend_hash_next_index_insert(data, &t); + } + ZVAL_LONG(&t, s->count); + zend_hash_next_index_insert(data, &t); + ZVAL_LONG(&t, s->mode); + zend_hash_next_index_insert(data, &t); + + return true; +} + +static bool unserialize(php_random_status *status, HashTable *data) +{ + php_random_status_state_mt19937 *s = status->state; + zval *t; + + for (uint32_t i = 0; i < MT_N; i++) { + t = zend_hash_index_find(data, i); + if (!t || Z_TYPE_P(t) != IS_STRING || Z_STRLEN_P(t) != (2 * sizeof(uint32_t))) { + return false; + } + if (!php_random_hex2bin_le(Z_STR_P(t), &s->state[i])) { + return false; + } + } + t = zend_hash_index_find(data, MT_N); + if (!t || Z_TYPE_P(t) != IS_LONG) { + return false; + } + s->count = Z_LVAL_P(t); + if (s->count > MT_N) { + return false; + } + + t = zend_hash_index_find(data, MT_N + 1); + if (!t || Z_TYPE_P(t) != IS_LONG) { + return false; + } + s->mode = Z_LVAL_P(t); + if (s->mode != MT_RAND_MT19937 && s->mode != MT_RAND_PHP) { + return false; + } + + return true; +} + +const php_random_algo php_random_algo_mt19937 = { + sizeof(uint32_t), + sizeof(php_random_status_state_mt19937), + seed, + generate, + range, + serialize, + unserialize +}; + +/* {{{ php_random_mt19937_seed_default */ +PHPAPI void php_random_mt19937_seed_default(php_random_status_state_mt19937 *state) +{ + zend_long seed = 0; + + if (php_random_bytes_silent(&seed, sizeof(zend_long)) == FAILURE) { + seed = GENERATE_SEED(); + } + + mt19937_seed_state(state, (uint64_t) seed); +} +/* }}} */ + +/* {{{ Random\Engine\Mt19937::__construct() */ +PHP_METHOD(Random_Engine_Mt19937, __construct) +{ + php_random_engine *engine = Z_RANDOM_ENGINE_P(ZEND_THIS); + php_random_status_state_mt19937 *state = engine->status->state; + zend_long seed, mode = MT_RAND_MT19937; + bool seed_is_null = true; + + ZEND_PARSE_PARAMETERS_START(0, 2) + Z_PARAM_OPTIONAL; + Z_PARAM_LONG_OR_NULL(seed, seed_is_null); + Z_PARAM_LONG(mode); + ZEND_PARSE_PARAMETERS_END(); + + switch (mode) { + case MT_RAND_MT19937: + state->mode = MT_RAND_MT19937; + break; + case MT_RAND_PHP: + state->mode = MT_RAND_PHP; + break; + default: + zend_argument_error(spl_ce_InvalidArgumentException, 2, "mode must be MT_RAND_MT19937 or MT_RAND_PHP"); + RETURN_THROWS(); + } + + if (seed_is_null) { + /* MT19937 has a very large state, uses CSPRNG for seeding only */ + if (php_random_bytes_silent(&seed, sizeof(zend_long)) == FAILURE) { + zend_throw_exception(spl_ce_RuntimeException, "Random number generation failed", 0); + RETURN_THROWS(); + } + } + + engine->algo->seed(engine->status, seed); +} +/* }}} */ + +/* {{{ Random\Engine\Mt19937::generate() */ +PHP_METHOD(Random_Engine_Mt19937, generate) +{ + php_random_engine *engine = Z_RANDOM_ENGINE_P(ZEND_THIS); + uint64_t generated; + size_t size; + zend_string *bytes; + + ZEND_PARSE_PARAMETERS_NONE(); + + generated = engine->algo->generate(engine->status); + size = engine->status->last_generated_size; + if (engine->status->last_unsafe) { + zend_throw_exception(spl_ce_RuntimeException, "Random number generation failed", 0); + RETURN_THROWS(); + } + + bytes = zend_string_alloc(size, false); + + /* Endianness safe copy */ + for (size_t i = 0; i < size; i++) { + ZSTR_VAL(bytes)[i] = (generated >> (i * 8)) & 0xff; + } + ZSTR_VAL(bytes)[size] = '\0'; + + RETURN_STR(bytes); +} +/* }}} */ + +/* {{{ Random\Engine\Mt19937::__serialize() */ +PHP_METHOD(Random_Engine_Mt19937, __serialize) +{ + php_random_engine *engine = Z_RANDOM_ENGINE_P(ZEND_THIS); + zval t; + + ZEND_PARSE_PARAMETERS_NONE(); + + array_init(return_value); + + /* members */ + ZVAL_ARR(&t, zend_std_get_properties(&engine->std)); + Z_TRY_ADDREF(t); + zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &t); + + /* state */ + array_init(&t); + if (!engine->algo->serialize(engine->status, Z_ARRVAL(t))) { + zend_throw_exception(NULL, "Engine serialize failed", 0); + RETURN_THROWS(); + } + zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &t); +} +/* }}} */ + +/* {{{ Random\Engine\Mt19937::__unserialize() */ +PHP_METHOD(Random_Engine_Mt19937, __unserialize) +{ + php_random_engine *engine = Z_RANDOM_ENGINE_P(ZEND_THIS); + HashTable *d; + zval *t; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY_HT(d); + ZEND_PARSE_PARAMETERS_END(); + + /* members */ + t = zend_hash_index_find(d, 0); + if (!t || Z_TYPE_P(t) != IS_ARRAY) { + zend_throw_exception(NULL, "Incomplete or ill-formed serialization data", 0); + RETURN_THROWS(); + } + object_properties_load(&engine->std, Z_ARRVAL_P(t)); + + /* state */ + t = zend_hash_index_find(d, 1); + if (!t || Z_TYPE_P(t) != IS_ARRAY) { + zend_throw_exception(NULL, "Incomplete or ill-formed serialization data", 0); + RETURN_THROWS(); + } + if (!engine->algo->unserialize(engine->status, Z_ARRVAL_P(t))) { + zend_throw_exception(NULL, "Engine unserialize failed", 0); + RETURN_THROWS(); + } +} +/* }}} */ + +/* {{{ Random\Engine\Mt19937::__debugInfo() */ +PHP_METHOD(Random_Engine_Mt19937, __debugInfo) +{ + php_random_engine *engine = Z_RANDOM_ENGINE_P(ZEND_THIS); + zval t; + + ZEND_PARSE_PARAMETERS_NONE(); + + if (!engine->std.properties) { + rebuild_object_properties(&engine->std); + } + ZVAL_ARR(return_value, zend_array_dup(engine->std.properties)); + + if (engine->algo->serialize) { + array_init(&t); + if (!engine->algo->serialize(engine->status, Z_ARRVAL(t))) { + zend_throw_exception(NULL, "Engine serialize failed", 0); + RETURN_THROWS(); + } + zend_hash_str_add(Z_ARR_P(return_value), "__states", strlen("__states"), &t); + } +} +/* }}} */ diff --git a/ext/random/engine_pcgoneseq128xslrr64.c b/ext/random/engine_pcgoneseq128xslrr64.c new file mode 100644 index 0000000000000..1d8644a67a87e --- /dev/null +++ b/ext/random/engine_pcgoneseq128xslrr64.c @@ -0,0 +1,191 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Go Kudo | + | | + | Based on code from: Melissa O'Neill | + +----------------------------------------------------------------------+ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "php.h" +#include "php_random.h" + +#include "ext/spl/spl_exceptions.h" +#include "Zend/zend_exceptions.h" + +static inline void step(php_random_status_state_pcgoneseq128xslrr64 *s) +{ + s->state = php_random_uint128_add( + php_random_uint128_multiply(s->state, php_random_uint128_constant(2549297995355413924ULL,4865540595714422341ULL)), + php_random_uint128_constant(6364136223846793005ULL,1442695040888963407ULL) + ); +} + +static inline void seed128(php_random_status *status, php_random_uint128_t seed) +{ + php_random_status_state_pcgoneseq128xslrr64 *s = status->state; + s->state = php_random_uint128_constant(0ULL, 0ULL); + step(s); + s->state = php_random_uint128_add(s->state, seed); + step(s); +} + +static void seed(php_random_status *status, uint64_t seed) +{ + seed128(status, php_random_uint128_constant(0ULL, seed)); +} + +static uint64_t generate(php_random_status *status) +{ + php_random_status_state_pcgoneseq128xslrr64 *s = status->state; + + step(s); + return php_random_pcgoneseq128xslrr64_rotr64(s->state); +} + +static zend_long range(php_random_status *status, zend_long min, zend_long max) +{ + return php_random_range(&php_random_algo_pcgoneseq128xslrr64, status, min, max); +} + +static bool serialize(php_random_status *status, HashTable *data) +{ + php_random_status_state_pcgoneseq128xslrr64 *s = status->state; + uint64_t u; + zval z; + + u = php_random_uint128_hi(s->state); + ZVAL_STR(&z, php_random_bin2hex_le(&u, sizeof(uint64_t))); + zend_hash_next_index_insert(data, &z); + + u = php_random_uint128_lo(s->state); + ZVAL_STR(&z, php_random_bin2hex_le(&u, sizeof(uint64_t))); + zend_hash_next_index_insert(data, &z); + + return true; +} + +static bool unserialize(php_random_status *status, HashTable *data) +{ + php_random_status_state_pcgoneseq128xslrr64 *s = status->state; + uint64_t u[2]; + zval *t; + + for (uint32_t i = 0; i < 2; i++) { + t = zend_hash_index_find(data, i); + if (!t || Z_TYPE_P(t) != IS_STRING || Z_STRLEN_P(t) != (2 * sizeof(uint64_t))) { + return false; + } + if (!php_random_hex2bin_le(Z_STR_P(t), &u[i])) { + return false; + } + } + s->state = php_random_uint128_constant(u[0], u[1]); + + return true; +} + +const php_random_algo php_random_algo_pcgoneseq128xslrr64 = { + sizeof(uint64_t), + sizeof(php_random_status_state_pcgoneseq128xslrr64), + seed, + generate, + range, + serialize, + unserialize +}; + +/* {{{ php_random_pcgoneseq128xslrr64_advance */ +PHPAPI void php_random_pcgoneseq128xslrr64_advance(php_random_status_state_pcgoneseq128xslrr64 *state, uint64_t advance) +{ + php_random_uint128_t + cur_mult = php_random_uint128_constant(2549297995355413924ULL,4865540595714422341ULL), + cur_plus = php_random_uint128_constant(6364136223846793005ULL,1442695040888963407ULL), + acc_mult = php_random_uint128_constant(0ULL, 1ULL), + acc_plus = php_random_uint128_constant(0ULL, 0ULL); + + while (advance > 0) { + if (advance & 1) { + acc_mult = php_random_uint128_multiply(acc_mult, cur_mult); + acc_plus = php_random_uint128_add(php_random_uint128_multiply(acc_plus, cur_mult), cur_plus); + } + cur_plus = php_random_uint128_multiply(php_random_uint128_add(cur_mult, php_random_uint128_constant(0ULL, 1ULL)), cur_plus); + cur_mult = php_random_uint128_multiply(cur_mult, cur_mult); + advance /= 2; + } + + state->state = php_random_uint128_add(php_random_uint128_multiply(acc_mult, state->state), acc_plus); +} +/* }}} */ + +/* {{{ Random\Engine\PcgOneseq128XslRr64::__construct */ +PHP_METHOD(Random_Engine_PcgOneseq128XslRr64, __construct) +{ + php_random_engine *engine = Z_RANDOM_ENGINE_P(ZEND_THIS); + php_random_status_state_pcgoneseq128xslrr64 *state = engine->status->state; + zend_string *str_seed = NULL; + zend_long int_seed = 0; + bool seed_is_null = true; + uint32_t i, j; + uint64_t t[2]; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL; + Z_PARAM_STR_OR_LONG_OR_NULL(str_seed, int_seed, seed_is_null); + ZEND_PARSE_PARAMETERS_END(); + + if (seed_is_null) { + if (php_random_bytes_silent(&state->state, sizeof(php_random_uint128_t)) == FAILURE) { + zend_throw_exception(spl_ce_RuntimeException, "Random number generation failed", 0); + RETURN_THROWS(); + } + } else { + if (str_seed) { + /* char (byte: 8 bit) * 16 = 128 bits */ + if (ZSTR_LEN(str_seed) == 16) { + /* Endianness safe copy */ + for (i = 0; i < 2; i++) { + t[i] = 0; + for (j = 0; j < 8; j++) { + t[i] += ((uint64_t) (unsigned char) ZSTR_VAL(str_seed)[(i * 8) + j]) << (j * 8); + } + } + seed128(engine->status, php_random_uint128_constant(t[0], t[1])); + } else { + zend_argument_value_error(1, "state strings must be 16 bytes"); + RETURN_THROWS(); + } + } else { + engine->algo->seed(engine->status, int_seed); + } + } +} +/* }}} */ + +/* {{{ Random\Engine\PcgOneseq128XslRr64::jump() */ +PHP_METHOD(Random_Engine_PcgOneseq128XslRr64, jump) +{ + php_random_engine *engine = Z_RANDOM_ENGINE_P(ZEND_THIS); + php_random_status_state_pcgoneseq128xslrr64 *state = engine->status->state; + zend_long advance = 0; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(advance); + ZEND_PARSE_PARAMETERS_END(); + + php_random_pcgoneseq128xslrr64_advance(state, advance); +} +/* }}} */ diff --git a/ext/random/engine_secure.c b/ext/random/engine_secure.c new file mode 100644 index 0000000000000..464472dbe04da --- /dev/null +++ b/ext/random/engine_secure.c @@ -0,0 +1,58 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Sammy Kaye Powers | + | Go Kudo | + +----------------------------------------------------------------------+ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "php.h" +#include "php_random.h" + +#include "ext/spl/spl_exceptions.h" +#include "Zend/zend_exceptions.h" + +static uint64_t generate(php_random_status *status) +{ + zend_ulong r = 0; + + if (php_random_bytes_silent(&r, sizeof(zend_ulong)) == FAILURE) { + status->last_unsafe = true; + } + + return r; +} + +static zend_long range(php_random_status *status, zend_long min, zend_long max) +{ + zend_long result; + + if (php_random_int_silent(min, max, &result) == FAILURE) { + status->last_unsafe = true; + } + + return result; +} + +const php_random_algo php_random_algo_secure = { + sizeof(zend_ulong), + 0, + NULL, + generate, + range, + NULL, + NULL +}; diff --git a/ext/random/engine_user.c b/ext/random/engine_user.c new file mode 100644 index 0000000000000..1249146b50539 --- /dev/null +++ b/ext/random/engine_user.c @@ -0,0 +1,70 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Go Kudo | + +----------------------------------------------------------------------+ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "php.h" +#include "php_random.h" + +static uint64_t generate(php_random_status *status) +{ + php_random_status_state_user *s = status->state; + uint64_t result = 0; + size_t size; + zval retval; + + zend_call_known_instance_method_with_0_params(s->generate_method, s->object, &retval); + + /* Store generated size in a state */ + size = Z_STRLEN(retval); + + /* Guard for over 64-bit results */ + if (size > sizeof(uint64_t)) { + size = sizeof(uint64_t); + } + status->last_generated_size = size; + + if (size > 0) { + /* Endianness safe copy */ + for (size_t i = 0; i < size; i++) { + result += ((uint64_t) (unsigned char) Z_STRVAL(retval)[i]) << (8 * i); + } + } else { + status->last_unsafe = true; + return 0; + } + + zval_ptr_dtor(&retval); + + return result; +} + +static zend_long range(php_random_status *status, zend_long min, zend_long max) +{ + return php_random_range(&php_random_algo_user, status, min, max); +} + +const php_random_algo php_random_algo_user = { + 0, + sizeof(php_random_status_state_user), + NULL, + generate, + range, + NULL, + NULL, +}; diff --git a/ext/random/engine_xoshiro256starstar.c b/ext/random/engine_xoshiro256starstar.c new file mode 100644 index 0000000000000..1f0065d976803 --- /dev/null +++ b/ext/random/engine_xoshiro256starstar.c @@ -0,0 +1,236 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Go Kudo | + | | + | Based on code from: David Blackman | + | Sebastiano Vigna | + +----------------------------------------------------------------------+ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "php.h" +#include "php_random.h" + +#include "ext/spl/spl_exceptions.h" +#include "Zend/zend_exceptions.h" + +static inline uint64_t splitmix64(uint64_t *seed) +{ + uint64_t r; + + r = (*seed += 0x9e3779b97f4a7c15ULL); + r = (r ^ (r >> 30)) * 0xbf58476d1ce4e5b9ULL; + r = (r ^ (r >> 27)) * 0x94d049bb133111ebULL; + return (r ^ (r >> 31)); +} + +static inline uint64_t rotl(const uint64_t x, int k) +{ + return (x << k) | (x >> (64 - k)); +} + +static inline uint64_t generate_state(php_random_status_state_xoshiro256starstar *s) +{ + const uint64_t r = rotl(s->state[1] * 5, 7) * 9; + const uint64_t t = s->state[1] << 17; + + s->state[2] ^= s->state[0]; + s->state[3] ^= s->state[1]; + s->state[1] ^= s->state[2]; + s->state[0] ^= s->state[3]; + + s->state[2] ^= t; + + s->state[3] = rotl(s->state[3], 45); + + return r; +} + +static inline void jump(php_random_status_state_xoshiro256starstar *state, const uint64_t *jmp) +{ + uint64_t s0 = 0, s1 = 0, s2 = 0, s3 = 0; + + for (uint32_t i = 0; i < 4; i++) { + for (uint32_t j = 0; j < 64; j++) { + if (jmp[i] & 1ULL << j) { + s0 ^= state->state[0]; + s1 ^= state->state[1]; + s2 ^= state->state[2]; + s3 ^= state->state[3]; + } + + generate_state(state); + } + } + + state->state[0] = s0; + state->state[1] = s1; + state->state[2] = s2; + state->state[3] = s3; +} + +static inline void seed256(php_random_status *status, uint64_t s0, uint64_t s1, uint64_t s2, uint64_t s3) +{ + php_random_status_state_xoshiro256starstar *s = status->state; + + s->state[0] = s0; + s->state[1] = s1; + s->state[2] = s2; + s->state[3] = s3; +} + +static void seed(php_random_status *status, uint64_t seed) +{ + uint64_t s[4]; + + s[0] = splitmix64(&seed); + s[1] = splitmix64(&seed); + s[2] = splitmix64(&seed); + s[3] = splitmix64(&seed); + + seed256(status, s[0], s[1], s[2], s[3]); +} + +static uint64_t generate(php_random_status *status) +{ + return generate_state(status->state); +} + +static zend_long range(php_random_status *status, zend_long min, zend_long max) +{ + return php_random_range(&php_random_algo_xoshiro256starstar, status, min, max); +} + +static bool serialize(php_random_status *status, HashTable *data) +{ + php_random_status_state_xoshiro256starstar *s = status->state; + zval t; + + for (uint32_t i = 0; i < 4; i++) { + ZVAL_STR(&t, php_random_bin2hex_le(&s->state[i], sizeof(uint64_t))); + zend_hash_next_index_insert(data, &t); + } + + return true; +} + +static bool unserialize(php_random_status *status, HashTable *data) +{ + php_random_status_state_xoshiro256starstar *s = status->state; + zval *t; + + for (uint32_t i = 0; i < 4; i++) { + t = zend_hash_index_find(data, i); + if (!t || Z_TYPE_P(t) != IS_STRING || Z_STRLEN_P(t) != (2 * sizeof(uint64_t))) { + return false; + } + if (!php_random_hex2bin_le(Z_STR_P(t), &s->state[i])) { + return false; + } + } + + return true; +} + +const php_random_algo php_random_algo_xoshiro256starstar = { + sizeof(uint64_t), + sizeof(php_random_status_state_xoshiro256starstar), + seed, + generate, + range, + serialize, + unserialize +}; + +PHPAPI void php_random_xoshiro256starstar_jump(php_random_status_state_xoshiro256starstar *state) +{ + static const uint64_t jmp[] = {0x180ec6d33cfd0aba, 0xd5a61266f0c9392c, 0xa9582618e03fc9aa, 0x39abdc4529b1661c}; + jump(state, jmp); +} + +PHPAPI void php_random_xoshiro256starstar_jump_long(php_random_status_state_xoshiro256starstar *state) +{ + static const uint64_t jmp[] = {0x76e15d3efefdcbbf, 0xc5004e441c522fb3, 0x77710069854ee241, 0x39109bb02acbe635}; + jump(state, jmp); +} + +/* {{{ Random\Engine\Xoshiro256StarStar::jump() */ +PHP_METHOD(Random_Engine_Xoshiro256StarStar, jump) +{ + php_random_engine *engine = Z_RANDOM_ENGINE_P(ZEND_THIS); + php_random_status_state_xoshiro256starstar *state = engine->status->state; + + ZEND_PARSE_PARAMETERS_NONE(); + + php_random_xoshiro256starstar_jump(state); +} +/* }}} */ + +/* {{{ Random\Engine\Xoshiro256StarStar::jumpLong() */ +PHP_METHOD(Random_Engine_Xoshiro256StarStar, jumpLong) +{ + php_random_engine *engine = Z_RANDOM_ENGINE_P(ZEND_THIS); + php_random_status_state_xoshiro256starstar *state = engine->status->state; + + ZEND_PARSE_PARAMETERS_NONE(); + + php_random_xoshiro256starstar_jump_long(state); +} +/* }}} */ + +/* {{{ Random\Engine\Xoshiro256StarStar::__construct */ +PHP_METHOD(Random_Engine_Xoshiro256StarStar, __construct) +{ + php_random_engine *engine = Z_RANDOM_ENGINE_P(ZEND_THIS); + php_random_status_state_xoshiro256starstar *state = engine->status->state; + zend_string *str_seed = NULL; + zend_long int_seed = 0; + bool seed_is_null = true; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL; + Z_PARAM_STR_OR_LONG_OR_NULL(str_seed, int_seed, seed_is_null); + ZEND_PARSE_PARAMETERS_END(); + + if (seed_is_null) { + if (php_random_bytes_silent(&state->state, 32) == FAILURE) { + zend_throw_exception(spl_ce_RuntimeException, "Random number generation failed", 0); + RETURN_THROWS(); + } + } else { + if (str_seed) { + /* char (byte: 8 bit) * 32 = 256 bits */ + if (ZSTR_LEN(str_seed) == 32) { + uint64_t t[4]; + + /* Endianness safe copy */ + for (uint32_t i = 0; i < 4; i++) { + t[i] = 0; + for (uint32_t j = 0; j < 8; j++) { + t[i] += ((uint64_t) (unsigned char) ZSTR_VAL(str_seed)[(i * 8) + j]) << (j * 8); + } + } + seed256(engine->status, t[0], t[1], t[2], t[3]); + } else { + zend_argument_value_error(1, "state strings must be 32 bytes"); + RETURN_THROWS(); + } + } else { + engine->algo->seed(engine->status, (uint64_t) int_seed); + } + } +} +/* }}} */ diff --git a/ext/random/php_random.h b/ext/random/php_random.h new file mode 100644 index 0000000000000..29dbccc91831c --- /dev/null +++ b/ext/random/php_random.h @@ -0,0 +1,323 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Rasmus Lerdorf | + | Zeev Suraski | + | Sascha Schumann | + | Pedro Melo | + | Sterling Hughes | + | Sammy Kaye Powers | + | Go Kudo | + | | + | Based on code from: Richard J. Wagner | + | Makoto Matsumoto | + | Takuji Nishimura | + | Shawn Cokus | + | David Blackman | + | Sebastiano Vigna | + | Melissa O'Neill | + +----------------------------------------------------------------------+ +*/ + +#ifndef PHP_RANDOM_H +# define PHP_RANDOM_H + +PHPAPI double php_combined_lcg(void); + +/* + * A bit of tricky math here. We want to avoid using a modulus because + * that simply tosses the high-order bits and might skew the distribution + * of random values over the range. Instead we map the range directly. + * + * We need to map the range from 0...M evenly to the range a...b + * Let n = the random number and n' = the mapped random number + * + * Then we have: n' = a + n(b-a)/M + * + * We have a problem here in that only n==M will get mapped to b which + * means the chances of getting b is much much less than getting any of + * the other values in the range. We can fix this by increasing our range + * artificially and using: + * + * n' = a + n(b-a+1)/M + * + * Now we only have a problem if n==M which would cause us to produce a + * number of b+1 which would be bad. So we bump M up by one to make sure + * this will never happen, and the final algorithm looks like this: + * + * n' = a + n(b-a+1)/(M+1) + * + * -RL + */ +# define RAND_RANGE_BADSCALING(__n, __min, __max, __tmax) \ + (__n) = (__min) + (zend_long) ((double) ( (double) (__max) - (__min) + 1.0) * ((__n) / ((__tmax) + 1.0))) + +# ifdef PHP_WIN32 +# define GENERATE_SEED() (((zend_long) (time(0) * GetCurrentProcessId())) ^ ((zend_long) (1000000.0 * php_combined_lcg()))) +# else +# define GENERATE_SEED() (((zend_long) (time(0) * getpid())) ^ ((zend_long) (1000000.0 * php_combined_lcg()))) +# endif + +# define PHP_MT_RAND_MAX ((zend_long) (0x7FFFFFFF)) /* (1<<31) - 1 */ + +# define MT_RAND_MT19937 0 +# define MT_RAND_PHP 1 + +# define MT_N (624) + +PHPAPI void php_mt_srand(uint32_t seed); +PHPAPI uint32_t php_mt_rand(void); +PHPAPI zend_long php_mt_rand_range(zend_long min, zend_long max); +PHPAPI zend_long php_mt_rand_common(zend_long min, zend_long max); + +# ifndef RAND_MAX +# define RAND_MAX PHP_MT_RAND_MAX +# endif + +# define PHP_RAND_MAX PHP_MT_RAND_MAX + +PHPAPI void php_srand(zend_long seed); +PHPAPI zend_long php_rand(void); + +# if !defined(__SIZEOF_INT128__) || defined(PHP_RANDOM_FORCE_EMULATE_128) +typedef struct _php_random_uint128_t { + uint64_t hi; + uint64_t lo; +} php_random_uint128_t; + +static inline uint64_t php_random_uint128_hi(php_random_uint128_t num) +{ + return num.hi; +} + +static inline uint64_t php_random_uint128_lo(php_random_uint128_t num) +{ + return num.lo; +} + +static inline php_random_uint128_t php_random_uint128_constant(uint64_t hi, uint64_t lo) +{ + php_random_uint128_t r; + + r.hi = hi; + r.lo = lo; + + return r; +} + +static inline php_random_uint128_t php_random_uint128_add(php_random_uint128_t num1, php_random_uint128_t num2) +{ + php_random_uint128_t r; + + r.lo = (num1.lo + num2.lo); + r.hi = (num1.hi + num2.hi + (r.lo < num1.lo)); + + return r; +} + +static inline php_random_uint128_t php_random_uint128_multiply(php_random_uint128_t num1, php_random_uint128_t num2) +{ + php_random_uint128_t r; + const uint64_t + x0 = num1.lo & 0xffffffffULL, + x1 = num1.lo >> 32, + y0 = num2.lo & 0xffffffffULL, + y1 = num2.lo >> 32, + z0 = (((x1 * y0) + (x0 * y0 >> 32)) & 0xffffffffULL) + x0 * y1; + + r.hi = num1.hi * num2.lo + num1.lo * num2.hi; + r.lo = num1.lo * num2.lo; + r.hi += x1 * y1 + ((x1 * y0 + (x0 * y0 >> 32)) >> 32) + (z0 >> 32); + + return r; +} + +static inline uint64_t php_random_pcgoneseq128xslrr64_rotr64(php_random_uint128_t num) +{ + const uint64_t + v = (num.hi ^ num.lo), + s = num.hi >> 58U; + + return (v >> s) | (v << ((-s) & 63)); +} +# else +typedef __uint128_t php_random_uint128_t; + +static inline uint64_t php_random_uint128_hi(php_random_uint128_t num) +{ + return (uint64_t) (num >> 64); +} + +static inline uint64_t php_random_uint128_lo(php_random_uint128_t num) +{ + return (uint64_t) num; +} + +static inline php_random_uint128_t php_random_uint128_constant(uint64_t hi, uint64_t lo) +{ + php_random_uint128_t r; + + r = ((php_random_uint128_t) hi << 64) + lo; + + return r; +} + +static inline php_random_uint128_t php_random_uint128_add(php_random_uint128_t num1, php_random_uint128_t num2) +{ + return num1 + num2; +} + +static inline php_random_uint128_t php_random_uint128_multiply(php_random_uint128_t num1, php_random_uint128_t num2) +{ + return num1 * num2; +} + +static inline uint64_t php_random_pcgoneseq128xslrr64_rotr64(php_random_uint128_t num) +{ + const uint64_t + v = ((uint64_t) (num >> 64U)) ^ (uint64_t) num, + s = num >> 122U; + + return (v >> s) | (v << ((-s) & 63)); +} +# endif + +# define php_random_bytes_throw(b, s) php_random_bytes((b), (s), 1) +# define php_random_bytes_silent(b, s) php_random_bytes((b), (s), 0) +# define php_random_int_throw(min, max, result) php_random_int((min), (max), (result), 1) +# define php_random_int_silent(min, max, result) php_random_int((min), (max), (result), 0) + +PHPAPI int php_random_bytes(void *bytes, size_t size, bool should_throw); +PHPAPI int php_random_int(zend_long min, zend_long max, zend_long *result, bool should_throw); + +typedef struct _php_random_status_ { + size_t last_generated_size; + bool last_unsafe; + void *state; +} php_random_status; + +typedef struct _php_random_status_state_combinedlcg { + int32_t state[2]; +} php_random_status_state_combinedlcg; + +typedef struct _php_random_status_state_mt19937 { + uint32_t state[MT_N]; + uint32_t count; + uint8_t mode; +} php_random_status_state_mt19937; + +typedef struct _php_random_status_state_pcgoneseq128xslrr64 { + php_random_uint128_t state; +} php_random_status_state_pcgoneseq128xslrr64; + +typedef struct _php_random_status_state_xoshiro256starstar { + uint64_t state[4]; +} php_random_status_state_xoshiro256starstar; + +typedef struct _php_random_status_state_user { + zend_object *object; + zend_function *generate_method; +} php_random_status_state_user; + +typedef struct _php_random_algo { + const size_t generate_size; + const size_t state_size; + void (*seed)(php_random_status *status, uint64_t seed); + uint64_t (*generate)(php_random_status *status); + zend_long (*range)(php_random_status *status, zend_long min, zend_long max); + bool (*serialize)(php_random_status *status, HashTable *data); + bool (*unserialize)(php_random_status *status, HashTable *data); +} php_random_algo; + +extern PHPAPI const php_random_algo php_random_algo_combinedlcg; +extern PHPAPI const php_random_algo php_random_algo_mt19937; +extern PHPAPI const php_random_algo php_random_algo_pcgoneseq128xslrr64; +extern PHPAPI const php_random_algo php_random_algo_xoshiro256starstar; +extern PHPAPI const php_random_algo php_random_algo_secure; +extern PHPAPI const php_random_algo php_random_algo_user; + +# define PHP_RANDOM_ALGO_IS_DYNAMIC(algo) ((algo)->generate_size == 0) + +typedef struct _php_random_engine { + const php_random_algo *algo; + php_random_status *status; + zend_object std; +} php_random_engine; + +typedef struct _php_random_randomizer { + const php_random_algo *algo; + php_random_status *status; + bool is_userland_algo; + zend_object std; +} php_random_randomizer; + +extern PHPAPI zend_class_entry *random_ce_Random_Engine; +extern PHPAPI zend_class_entry *random_ce_Random_CryptoSafeEngine; + +extern PHPAPI zend_class_entry *random_ce_Random_Engine_PcgOneseq128XslRr64; +extern PHPAPI zend_class_entry *random_ce_Random_Engine_Mt19937; +extern PHPAPI zend_class_entry *random_ce_Random_Engine_Xoshiro256StarStar; +extern PHPAPI zend_class_entry *random_ce_Random_Engine_Secure; +extern PHPAPI zend_class_entry *random_ce_Random_Randomizer; + +static inline php_random_engine *php_random_engine_from_obj(zend_object *object) { + return (php_random_engine *)((char *)(object) - XtOffsetOf(php_random_engine, std)); +} + +static inline php_random_randomizer *php_random_randomizer_from_obj(zend_object *object) { + return (php_random_randomizer *)((char *)(object) - XtOffsetOf(php_random_randomizer, std)); +} + +# define Z_RANDOM_ENGINE_P(zval) php_random_engine_from_obj(Z_OBJ_P(zval)) + +# define Z_RANDOM_RANDOMIZER_P(zval) php_random_randomizer_from_obj(Z_OBJ_P(zval)); + +PHPAPI php_random_status *php_random_status_alloc(const php_random_algo *algo, const bool persistent); +PHPAPI php_random_status *php_random_status_copy(const php_random_algo *algo, php_random_status *old_status, php_random_status *new_status); +PHPAPI void php_random_status_free(php_random_status *status, const bool persistent); +PHPAPI php_random_engine *php_random_engine_common_init(zend_class_entry *ce, zend_object_handlers *handlers, const php_random_algo *algo); +PHPAPI void php_random_engine_common_free_object(zend_object *object); +PHPAPI zend_object *php_random_engine_common_clone_object(zend_object *object); +PHPAPI zend_long php_random_range(const php_random_algo *algo, php_random_status *status, zend_long min, zend_long max); +PHPAPI const php_random_algo *php_random_default_algo(void); +PHPAPI php_random_status *php_random_default_status(void); + +PHPAPI zend_string *php_random_bin2hex_le(const void *ptr, const size_t len); +PHPAPI bool php_random_hex2bin_le(zend_string *hexstr, void *dest); + +PHPAPI void php_random_combinedlcg_seed_default(php_random_status_state_combinedlcg *state); + +PHPAPI void php_random_mt19937_seed_default(php_random_status_state_mt19937 *state); + +PHPAPI void php_random_pcgoneseq128xslrr64_advance(php_random_status_state_pcgoneseq128xslrr64 *state, uint64_t advance); + +PHPAPI void php_random_xoshiro256starstar_jump(php_random_status_state_xoshiro256starstar *state); +PHPAPI void php_random_xoshiro256starstar_jump_long(php_random_status_state_xoshiro256starstar *state); + +extern zend_module_entry random_module_entry; +# define phpext_random_ptr &random_module_entry + +PHP_MINIT_FUNCTION(random); +PHP_MSHUTDOWN_FUNCTION(random); +PHP_RINIT_FUNCTION(random); + +ZEND_BEGIN_MODULE_GLOBALS(random) + php_random_status *combined_lcg; + bool combined_lcg_seeded; + php_random_status *mt19937; + bool mt19937_seeded; + int random_fd; +ZEND_END_MODULE_GLOBALS(random) + +# define RANDOM_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(random, v) + +#endif /* PHP_RANDOM_H */ diff --git a/ext/random/random.c b/ext/random/random.c new file mode 100644 index 0000000000000..f8a94e65ee34e --- /dev/null +++ b/ext/random/random.c @@ -0,0 +1,911 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Sammy Kaye Powers | + | Go Kudo | + +----------------------------------------------------------------------+ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include + +#include "php.h" + +#include "ext/spl/spl_exceptions.h" +#include "Zend/zend_exceptions.h" + +#include "php_random.h" + +#if HAVE_UNISTD_H +# include +#endif + +#ifdef PHP_WIN32 +# include "win32/time.h" +# include "win32/winutil.h" +# include +#else +# include +#endif + +#ifdef __linux__ +# include +#endif + +#if HAVE_SYS_PARAM_H +# include +# if (__FreeBSD__ && __FreeBSD_version > 1200000) || (__DragonFly__ && __DragonFly_version >= 500700) || defined(__sun) +# include +# endif +#endif + +#if HAVE_COMMONCRYPTO_COMMONRANDOM_H +# include +# include +#endif + +#if __has_feature(memory_sanitizer) +# include +#endif + +#include "random_arginfo.h" + +ZEND_DECLARE_MODULE_GLOBALS(random) + +PHPAPI zend_class_entry *random_ce_Random_Engine; +PHPAPI zend_class_entry *random_ce_Random_CryptoSafeEngine; + +PHPAPI zend_class_entry *random_ce_Random_Engine_Mt19937; +PHPAPI zend_class_entry *random_ce_Random_Engine_PcgOneseq128XslRr64; +PHPAPI zend_class_entry *random_ce_Random_Engine_Xoshiro256StarStar; +PHPAPI zend_class_entry *random_ce_Random_Engine_Secure; +PHPAPI zend_class_entry *random_ce_Random_Randomizer; + +static zend_object_handlers random_engine_mt19937_object_handlers; +static zend_object_handlers random_engine_pcgoneseq128xslrr64_object_handlers; +static zend_object_handlers random_engine_xoshiro256starstar_object_handlers; +static zend_object_handlers random_engine_secure_object_handlers; +static zend_object_handlers random_randomizer_object_handlers; + +static inline uint32_t rand_range32(const php_random_algo *algo, php_random_status *status, uint32_t umax) +{ + uint32_t result, limit, r; + size_t total_size = 0; + uint32_t count = 0; + + result = algo->generate(status); + total_size = status->last_generated_size; + if (status->last_unsafe) { + return 0; + } + if (total_size < sizeof(uint32_t)) { + r = algo->generate(status); + result = (result << status->last_generated_size) | r; + total_size += status->last_generated_size; + if (status->last_unsafe) { + return 0; + } + } + + /* Special case where no modulus is required */ + if (UNEXPECTED(umax == UINT32_MAX)) { + return true; + } + + /* Increment the max so range is inclusive of max */ + umax++; + + /* Powers of two are not biased */ + if ((umax & (umax - 1)) == 0) { + return result & (umax - 1); + } + + /* Ceiling under which UINT32_MAX % max == 0 */ + limit = UINT32_MAX - (UINT32_MAX % umax) - 1; + + /* Discard numbers over the limit to avoid modulo bias */ + while (UNEXPECTED(result > limit)) { + /* If the requirements cannot be met in a cycles, return fail */ + if (++count > 50) { + status->last_unsafe = true; + return 0; + } + + result = algo->generate(status); + total_size = status->last_generated_size; + if (status->last_unsafe) { + return 0; + } + while (total_size < sizeof(uint32_t)) { + r = algo->generate(status); + result = (result << status->last_generated_size) | r; + total_size += status->last_generated_size; + if (status->last_unsafe) { + return 0; + } + } + } + + return result % umax; +} + +static inline uint64_t rand_range64(const php_random_algo *algo, php_random_status *status, uint64_t umax) +{ + uint64_t result, limit, r; + size_t total_size = 0; + uint32_t count = 0; + + result = algo->generate(status); + total_size = status->last_generated_size; + if (status->last_unsafe) { + return 0; + } + if (total_size < sizeof(uint64_t)) { + r = algo->generate(status); + result = (result << status->last_generated_size) | r; + total_size += status->last_generated_size; + if (status->last_unsafe) { + return 0; + } + } + + /* Special case where no modulus is required */ + if (UNEXPECTED(umax == UINT64_MAX)) { + return result; + } + + /* Increment the max so range is inclusive of max */ + umax++; + + /* Powers of two are not biased */ + if ((umax & (umax - 1)) == 0) { + return result & (umax - 1); + } + + /* Ceiling under which UINT64_MAX % max == 0 */ + limit = UINT64_MAX - (UINT64_MAX % umax) - 1; + + /* Discard numbers over the limit to avoid modulo bias */ + while (UNEXPECTED(result > limit)) { + /* If the requirements cannot be met in a cycles, return fail */ + if (++count > 50) { + status->last_unsafe = true; + return 0; + } + + result = algo->generate(status); + total_size = status->last_generated_size; + if (status->last_unsafe) { + return 0; + } + while (total_size < sizeof(uint64_t)) { + r = algo->generate(status); + result = (result << status->last_generated_size) | r; + total_size += status->last_generated_size; + if (status->last_unsafe) { + return 0; + } + } + } + + return result % umax; +} + +static zend_object *php_random_engine_mt19937_new(zend_class_entry *ce) +{ + return &php_random_engine_common_init(ce, &random_engine_mt19937_object_handlers, &php_random_algo_mt19937)->std; +} + +static zend_object *php_random_engine_pcgoneseq128xslrr64_new(zend_class_entry *ce) +{ + return &php_random_engine_common_init(ce, &random_engine_pcgoneseq128xslrr64_object_handlers, &php_random_algo_pcgoneseq128xslrr64)->std; +} + +static zend_object *php_random_engine_xoshiro256starstar_new(zend_class_entry *ce) +{ + return &php_random_engine_common_init(ce, &random_engine_xoshiro256starstar_object_handlers, &php_random_algo_xoshiro256starstar)->std; +} + +static zend_object *php_random_engine_secure_new(zend_class_entry *ce) +{ + return &php_random_engine_common_init(ce, &random_engine_secure_object_handlers, &php_random_algo_secure)->std; +} + +static zend_object *php_random_randomizer_new(zend_class_entry *ce) +{ + php_random_randomizer *randomizer = zend_object_alloc(sizeof(php_random_randomizer), ce); + + zend_object_std_init(&randomizer->std, ce); + object_properties_init(&randomizer->std, ce); + + randomizer->std.handlers = &random_randomizer_object_handlers; + + return &randomizer->std; +} + +static void randomizer_free_obj(zend_object *object) { + php_random_randomizer *randomizer = php_random_randomizer_from_obj(object); + + if (randomizer->is_userland_algo && randomizer->status) { + php_random_status_free(randomizer->status, false); + } + + zend_object_std_dtor(&randomizer->std); +} + +PHPAPI php_random_status *php_random_status_alloc(const php_random_algo *algo, const bool persistent) +{ + php_random_status *status = pecalloc(1, sizeof(php_random_status), persistent); + + status->last_generated_size = algo->generate_size; + status->last_unsafe = false; + status->state = algo->state_size > 0 ? pecalloc(1, algo->state_size, persistent) : NULL; + + return status; +} + +PHPAPI php_random_status *php_random_status_copy(const php_random_algo *algo, php_random_status *old_status, php_random_status *new_status) +{ + new_status->last_generated_size = old_status->last_generated_size; + new_status->last_unsafe = old_status->last_unsafe; + new_status->state = memcpy(new_status->state, old_status->state, algo->state_size); + + return new_status; +} + +PHPAPI void php_random_status_free(php_random_status *status, const bool persistent) +{ + if (status->state) { + pefree(status->state, persistent); + } + pefree(status, persistent); +} + +PHPAPI php_random_engine *php_random_engine_common_init(zend_class_entry *ce, zend_object_handlers *handlers, const php_random_algo *algo) +{ + php_random_engine *engine = zend_object_alloc(sizeof(php_random_engine), ce); + + zend_object_std_init(&engine->std, ce); + object_properties_init(&engine->std, ce); + + engine->algo = algo; + engine->status = php_random_status_alloc(engine->algo, false); + engine->std.handlers = handlers; + + return engine; +} + +PHPAPI void php_random_engine_common_free_object(zend_object *object) +{ + php_random_engine *engine = php_random_engine_from_obj(object); + + if (engine->status) { + php_random_status_free(engine->status, false); + } + + zend_object_std_dtor(object); +} + +PHPAPI zend_object *php_random_engine_common_clone_object(zend_object *object) +{ + php_random_engine *old_engine = php_random_engine_from_obj(object); + php_random_engine *new_engine = php_random_engine_from_obj(old_engine->std.ce->create_object(old_engine->std.ce)); + + new_engine->algo = old_engine->algo; + if (old_engine->status) { + new_engine->status = php_random_status_copy(old_engine->algo, old_engine->status, new_engine->status); + } + + zend_objects_clone_members(&new_engine->std, &old_engine->std); + + return &new_engine->std; +} + +/* {{{ php_random_range */ +PHPAPI zend_long php_random_range(const php_random_algo *algo, php_random_status *status, zend_long min, zend_long max) +{ + zend_ulong umax = max - min; + + if (PHP_RANDOM_ALGO_IS_DYNAMIC(algo) || algo->generate_size > sizeof(uint32_t) || umax > UINT32_MAX) { + return (zend_long) rand_range64(algo, status, umax) + min; + } + + return (zend_long) rand_range32(algo, status, umax) + min; +} +/* }}} */ + +/* {{{ php_random_default_algo */ +PHPAPI const php_random_algo *php_random_default_algo(void) +{ + return &php_random_algo_mt19937; +} +/* }}} */ + +/* {{{ php_random_default_status */ +PHPAPI php_random_status *php_random_default_status(void) +{ + php_random_status *status = RANDOM_G(mt19937); + + if (!RANDOM_G(mt19937_seeded)) { + php_random_mt19937_seed_default(status->state); + RANDOM_G(mt19937_seeded) = true; + } + + return status; +} +/* }}} */ + +/* this is read-only, so it's ok */ +ZEND_SET_ALIGNED(16, static const char hexconvtab[]) = "0123456789abcdef"; + +/* {{{ php_random_bin2hex_le */ +/* stolen from standard/string.c */ +PHPAPI zend_string *php_random_bin2hex_le(const void *ptr, const size_t len) +{ + zend_string *str; + size_t i; + + str = zend_string_safe_alloc(len, 2 * sizeof(char), 0, 0); + + i = 0; +#ifdef WORDS_BIGENDIAN + /* force little endian */ + for (zend_long j = (len - 1); 0 <= j; j--) { +#else + for (zend_long j = 0; j < len; j++) { +#endif + ZSTR_VAL(str)[i++] = hexconvtab[((unsigned char *) ptr)[j] >> 4]; + ZSTR_VAL(str)[i++] = hexconvtab[((unsigned char *) ptr)[j] & 15]; + } + ZSTR_VAL(str)[i] = '\0'; + + return str; +} +/* }}} */ + +/* {{{ php_random_hex2bin_le */ +/* stolen from standard/string.c */ +PHPAPI bool php_random_hex2bin_le(zend_string *hexstr, void *dest) +{ + size_t len = hexstr->len >> 1; + unsigned char *str = (unsigned char *) hexstr->val, c, l, d; + unsigned char *ptr = (unsigned char *) dest; + int is_letter, i = 0; + +#ifdef WORDS_BIGENDIAN + /* force little endian */ + for (zend_long j = (len - 1); 0 <= j; j--) { +#else + for (zend_long j = 0; j < len; j++) { +#endif + c = str[i++]; + l = c & ~0x20; + is_letter = ((uint32_t) ((l - 'A') ^ (l - 'F' - 1))) >> (8 * sizeof(uint32_t) - 1); + + /* basically (c >= '0' && c <= '9') || (l >= 'A' && l <= 'F') */ + if (EXPECTED((((c ^ '0') - 10) >> (8 * sizeof(uint32_t) - 1)) | is_letter)) { + d = (l - 0x10 - 0x27 * is_letter) << 4; + } else { + return false; + } + c = str[i++]; + l = c & ~0x20; + is_letter = ((uint32_t) ((l - 'A') ^ (l - 'F' - 1))) >> (8 * sizeof(uint32_t) - 1); + if (EXPECTED((((c ^ '0') - 10) >> (8 * sizeof(uint32_t) - 1)) | is_letter)) { + d |= l - 0x10 - 0x27 * is_letter; + } else { + return false; + } + ptr[j] = d; + } + return true; +} +/* }}} */ + +/* {{{ php_combined_lcg */ +PHPAPI double php_combined_lcg(void) +{ + php_random_status *status = RANDOM_G(combined_lcg); + + if (!RANDOM_G(combined_lcg_seeded)) { + php_random_combinedlcg_seed_default(status->state); + RANDOM_G(combined_lcg_seeded) = true; + } + + return php_random_algo_combinedlcg.generate(status) * 4.656613e-10; +} +/* }}} */ + +/* {{{ php_mt_srand */ +PHPAPI void php_mt_srand(uint32_t seed) +{ + /* Seed the generator with a simple uint32 */ + php_random_algo_mt19937.seed(php_random_default_status(), (zend_long) seed); +} +/* }}} */ + +/* {{{ php_mt_rand */ +PHPAPI uint32_t php_mt_rand(void) +{ + return (uint32_t) php_random_algo_mt19937.generate(php_random_default_status()); +} +/* }}} */ + +/* {{{ php_mt_rand_range */ +PHPAPI zend_long php_mt_rand_range(zend_long min, zend_long max) +{ + return php_random_algo_mt19937.range(php_random_default_status(), min, max); +} +/* }}} */ + +/* {{{ php_mt_rand_common + * rand() allows min > max, mt_rand does not */ +PHPAPI zend_long php_mt_rand_common(zend_long min, zend_long max) +{ + return php_mt_rand_range(min, max); +} +/* }}} */ + +/* {{{ php_srand */ +PHPAPI void php_srand(zend_long seed) +{ + php_mt_srand((uint32_t) seed); +} +/* }}} */ + +/* {{{ php_rand */ +PHPAPI zend_long php_rand(void) +{ + return php_mt_rand(); +} +/* }}} */ + +/* {{{ php_random_bytes */ +PHPAPI int php_random_bytes(void *bytes, size_t size, bool should_throw) +{ +#ifdef PHP_WIN32 + /* Defer to CryptGenRandom on Windows */ + if (php_win32_get_random_bytes(bytes, size) == FAILURE) { + if (should_throw) { + zend_throw_exception(zend_ce_exception, "Could not gather sufficient random data", 0); + } + return FAILURE; + } +#elif HAVE_COMMONCRYPTO_COMMONRANDOM_H + /* + * Purposely prioritized upon arc4random_buf for modern macOs releases + * arc4random api on this platform uses `ccrng_generate` which returns + * a status but silented to respect the "no fail" arc4random api interface + * the vast majority of the time, it works fine ; but better make sure we catch failures + */ + if (CCRandomGenerateBytes(bytes, size) != kCCSuccess) { + if (should_throw) { + zend_throw_exception(zend_ce_exception, "Error generating bytes", 0); + } + return FAILURE; + } +#elif HAVE_DECL_ARC4RANDOM_BUF && ((defined(__OpenBSD__) && OpenBSD >= 201405) || (defined(__NetBSD__) && __NetBSD_Version__ >= 700000001) || defined(__APPLE__) || defined(__GLIBC__)) + arc4random_buf(bytes, size); +#else + size_t read_bytes = 0; + ssize_t n; +# if (defined(__linux__) && defined(SYS_getrandom)) || (defined(__FreeBSD__) && __FreeBSD_version >= 1200000) || (defined(__DragonFly__) && __DragonFly_version >= 500700) || defined(__sun) + /* Linux getrandom(2) syscall or FreeBSD/DragonFlyBSD getrandom(2) function*/ + /* Keep reading until we get enough entropy */ + while (read_bytes < size) { + /* Below, (bytes + read_bytes) is pointer arithmetic. + + bytes read_bytes size + | | | + [#######=============] (we're going to write over the = region) + \\\\\\\\\\\\\ + amount_to_read + */ + size_t amount_to_read = size - read_bytes; +# if defined(__linux__) + n = syscall(SYS_getrandom, bytes + read_bytes, amount_to_read, 0); +# else + n = getrandom(bytes + read_bytes, amount_to_read, 0); +# endif + + if (n == -1) { + if (errno == ENOSYS) { + /* This can happen if PHP was compiled against a newer kernel where getrandom() + * is available, but then runs on an older kernel without getrandom(). If this + * happens we simply fall back to reading from /dev/urandom. */ + ZEND_ASSERT(read_bytes == 0); + break; + } else if (errno == EINTR || errno == EAGAIN) { + /* Try again */ + continue; + } else { + /* If the syscall fails, fall back to reading from /dev/urandom */ + break; + } + } + +# if __has_feature(memory_sanitizer) + /* MSan does not instrument manual syscall invocations. */ + __msan_unpoison(bytes + read_bytes, n); +# endif + read_bytes += (size_t) n; + } +# endif + if (read_bytes < size) { + int fd = RANDOM_G(random_fd); + struct stat st; + + if (fd < 0) { +# if HAVE_DEV_URANDOM + fd = open("/dev/urandom", O_RDONLY); +# endif + if (fd < 0) { + if (should_throw) { + zend_throw_exception(zend_ce_exception, "Cannot open source device", 0); + } + return FAILURE; + } + /* Does the file exist and is it a character device? */ + if (fstat(fd, &st) != 0 || +# ifdef S_ISNAM + !(S_ISNAM(st.st_mode) || S_ISCHR(st.st_mode)) +# else + !S_ISCHR(st.st_mode) +# endif + ) { + close(fd); + if (should_throw) { + zend_throw_exception(zend_ce_exception, "Error reading from source device", 0); + } + return FAILURE; + } + RANDOM_G(random_fd) = fd; + } + + for (read_bytes = 0; read_bytes < size; read_bytes += (size_t) n) { + n = read(fd, bytes + read_bytes, size - read_bytes); + if (n <= 0) { + break; + } + } + + if (read_bytes < size) { + if (should_throw) { + zend_throw_exception(zend_ce_exception, "Could not gather sufficient random data", 0); + } + return FAILURE; + } + } +#endif + + return SUCCESS; +} +/* }}} */ + +/* {{{ php_random_int */ +PHPAPI int php_random_int(zend_long min, zend_long max, zend_long *result, bool should_throw) +{ + zend_ulong umax; + zend_ulong trial; + + if (min == max) { + *result = min; + return SUCCESS; + } + + umax = (zend_ulong) max - (zend_ulong) min; + + if (php_random_bytes(&trial, sizeof(trial), should_throw) == FAILURE) { + return FAILURE; + } + + /* Special case where no modulus is required */ + if (umax == ZEND_ULONG_MAX) { + *result = (zend_long)trial; + return SUCCESS; + } + + /* Increment the max so the range is inclusive of max */ + umax++; + + /* Powers of two are not biased */ + if ((umax & (umax - 1)) != 0) { + /* Ceiling under which ZEND_LONG_MAX % max == 0 */ + zend_ulong limit = ZEND_ULONG_MAX - (ZEND_ULONG_MAX % umax) - 1; + + /* Discard numbers over the limit to avoid modulo bias */ + while (trial > limit) { + if (php_random_bytes(&trial, sizeof(trial), should_throw) == FAILURE) { + return FAILURE; + } + } + } + + *result = (zend_long)((trial % umax) + min); + return SUCCESS; +} +/* }}} */ + +/* {{{ Returns a value from the combined linear congruential generator */ +PHP_FUNCTION(lcg_value) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + RETURN_DOUBLE(php_combined_lcg()); +} +/* }}} */ + +/* {{{ Seeds Mersenne Twister random number generator */ +PHP_FUNCTION(mt_srand) +{ + zend_long seed = 0; + zend_long mode = MT_RAND_MT19937; + php_random_status *status = RANDOM_G(mt19937); + php_random_status_state_mt19937 *state = status->state; + + ZEND_PARSE_PARAMETERS_START(0, 2) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(seed) + Z_PARAM_LONG(mode) + ZEND_PARSE_PARAMETERS_END(); + + state->mode = mode; + + if (ZEND_NUM_ARGS() == 0) { + php_random_mt19937_seed_default(status->state); + } else { + php_random_algo_mt19937.seed(status, (uint64_t) seed); + } + RANDOM_G(mt19937_seeded) = true; +} +/* }}} */ + +/* {{{ Returns a random number from Mersenne Twister */ +PHP_FUNCTION(mt_rand) +{ + zend_long min, max; + int argc = ZEND_NUM_ARGS(); + + if (argc == 0) { + /* genrand_int31 in mt19937ar.c performs a right shift */ + RETURN_LONG(php_mt_rand() >> 1); + } + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_LONG(min) + Z_PARAM_LONG(max) + ZEND_PARSE_PARAMETERS_END(); + + if (UNEXPECTED(max < min)) { + zend_argument_value_error(2, "must be greater than or equal to argument #1 ($min)"); + RETURN_THROWS(); + } + + RETURN_LONG(php_mt_rand_common(min, max)); +} +/* }}} */ + +/* {{{ Returns the maximum value a random number from Mersenne Twister can have */ +PHP_FUNCTION(mt_getrandmax) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + /* + * Melo: it could be 2^^32 but we only use 2^^31 to maintain + * compatibility with the previous php_rand + */ + RETURN_LONG(PHP_MT_RAND_MAX); /* 2^^31 */ +} +/* }}} */ + +/* {{{ Returns a random number from Mersenne Twister */ +PHP_FUNCTION(rand) +{ + zend_long min, max; + int argc = ZEND_NUM_ARGS(); + + if (argc == 0) { + /* genrand_int31 in mt19937ar.c performs a right shift */ + RETURN_LONG(php_mt_rand() >> 1); + } + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_LONG(min) + Z_PARAM_LONG(max) + ZEND_PARSE_PARAMETERS_END(); + + if (max < min) { + RETURN_LONG(php_mt_rand_common(max, min)); + } + + RETURN_LONG(php_mt_rand_common(min, max)); +} +/* }}} */ + +/* {{{ Return an arbitrary length of pseudo-random bytes as binary string */ +PHP_FUNCTION(random_bytes) +{ + zend_long size; + zend_string *bytes; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(size) + ZEND_PARSE_PARAMETERS_END(); + + if (size < 1) { + zend_argument_value_error(1, "must be greater than 0"); + RETURN_THROWS(); + } + + bytes = zend_string_alloc(size, 0); + + if (php_random_bytes_throw(ZSTR_VAL(bytes), size) == FAILURE) { + zend_string_release_ex(bytes, 0); + RETURN_THROWS(); + } + + ZSTR_VAL(bytes)[size] = '\0'; + + RETURN_STR(bytes); +} +/* }}} */ + +/* {{{ Return an arbitrary pseudo-random integer */ +PHP_FUNCTION(random_int) +{ + zend_long min, max, result; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_LONG(min) + Z_PARAM_LONG(max) + ZEND_PARSE_PARAMETERS_END(); + + if (min > max) { + zend_argument_value_error(1, "must be less than or equal to argument #2 ($max)"); + RETURN_THROWS(); + } + + if (php_random_int_throw(min, max, &result) == FAILURE) { + RETURN_THROWS(); + } + + RETURN_LONG(result); +} +/* }}} */ + +/* {{{ PHP_MINIT_FUNCTION */ +PHP_MINIT_FUNCTION(random) +{ + /* Random\Engine */ + random_ce_Random_Engine = register_class_Random_Engine(); + + /* Random\CryptoSafeEngine */ + random_ce_Random_CryptoSafeEngine = register_class_Random_CryptoSafeEngine(random_ce_Random_Engine); + + /* Random\Engine\Mt19937 */ + random_ce_Random_Engine_Mt19937 = register_class_Random_Engine_Mt19937(random_ce_Random_Engine); + random_ce_Random_Engine_Mt19937->create_object = php_random_engine_mt19937_new; + memcpy(&random_engine_mt19937_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); + random_engine_mt19937_object_handlers.offset = XtOffsetOf(php_random_engine, std); + random_engine_mt19937_object_handlers.free_obj = php_random_engine_common_free_object; + random_engine_mt19937_object_handlers.clone_obj = php_random_engine_common_clone_object; + + /* Random\Engine\PcgOnseq128XslRr64 */ + random_ce_Random_Engine_PcgOneseq128XslRr64 = register_class_Random_Engine_PcgOneseq128XslRr64(random_ce_Random_Engine); + random_ce_Random_Engine_PcgOneseq128XslRr64->create_object = php_random_engine_pcgoneseq128xslrr64_new; + memcpy(&random_engine_pcgoneseq128xslrr64_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); + random_engine_pcgoneseq128xslrr64_object_handlers.offset = XtOffsetOf(php_random_engine, std); + random_engine_pcgoneseq128xslrr64_object_handlers.free_obj = php_random_engine_common_free_object; + random_engine_pcgoneseq128xslrr64_object_handlers.clone_obj = php_random_engine_common_clone_object; + + /* Random\Engine\Xoshiro256StarStar */ + random_ce_Random_Engine_Xoshiro256StarStar = register_class_Random_Engine_Xoshiro256StarStar(random_ce_Random_Engine); + random_ce_Random_Engine_Xoshiro256StarStar->create_object = php_random_engine_xoshiro256starstar_new; + memcpy(&random_engine_xoshiro256starstar_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); + random_engine_xoshiro256starstar_object_handlers.offset = XtOffsetOf(php_random_engine, std); + random_engine_xoshiro256starstar_object_handlers.free_obj = php_random_engine_common_free_object; + random_engine_xoshiro256starstar_object_handlers.clone_obj = php_random_engine_common_clone_object; + + /* Random\Engine\Secure */ + random_ce_Random_Engine_Secure = register_class_Random_Engine_Secure(random_ce_Random_CryptoSafeEngine); + random_ce_Random_Engine_Secure->create_object = php_random_engine_secure_new; + memcpy(&random_engine_secure_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); + random_engine_secure_object_handlers.offset = XtOffsetOf(php_random_engine, std); + random_engine_secure_object_handlers.free_obj = php_random_engine_common_free_object; + random_engine_secure_object_handlers.clone_obj = NULL; + + /* Random\Randomizer */ + random_ce_Random_Randomizer = register_class_Random_Randomizer(); + random_ce_Random_Randomizer->create_object = php_random_randomizer_new; + memcpy(&random_randomizer_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); + random_randomizer_object_handlers.offset = XtOffsetOf(php_random_randomizer, std); + random_randomizer_object_handlers.free_obj = randomizer_free_obj; + random_randomizer_object_handlers.clone_obj = NULL; + + REGISTER_LONG_CONSTANT("MT_RAND_MT19937", MT_RAND_MT19937, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("MT_RAND_PHP", MT_RAND_PHP, CONST_CS | CONST_PERSISTENT); + + RANDOM_G(random_fd) = -1; + + RANDOM_G(combined_lcg) = php_random_status_alloc(&php_random_algo_combinedlcg, true); + RANDOM_G(combined_lcg_seeded) = false; + + RANDOM_G(mt19937) = php_random_status_alloc(&php_random_algo_mt19937, true); + RANDOM_G(mt19937_seeded) = false; + + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MSHUTDOWN_FUNCTION */ +PHP_MSHUTDOWN_FUNCTION(random) +{ + if (RANDOM_G(random_fd) > 0) { + close(RANDOM_G(random_fd)); + RANDOM_G(random_fd) = -1; + } + + php_random_status_free(RANDOM_G(combined_lcg), true); + RANDOM_G(combined_lcg) = NULL; + + php_random_status_free(RANDOM_G(mt19937), true); + RANDOM_G(mt19937) = NULL; + + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_RINIT_FUNCTION */ +PHP_RINIT_FUNCTION(random) +{ +#if defined(ZTS) && defined(COMPILE_DL_RANDOM) + ZEND_TSRMLS_CACHE_UPDATE(); +#endif + + RANDOM_G(combined_lcg_seeded) = false; + RANDOM_G(mt19937_seeded) = false; + + return SUCCESS; +} +/* }}} */ + +/* {{{ random_module_entry */ +zend_module_entry random_module_entry = { + STANDARD_MODULE_HEADER, + "random", /* Extension name */ + ext_functions, /* zend_function_entry */ + PHP_MINIT(random), /* PHP_MINIT - Module initialization */ + PHP_MSHUTDOWN(random), /* PHP_MSHUTDOWN - Module shutdown */ + PHP_RINIT(random), /* PHP_RINIT - Request initialization */ + NULL, /* PHP_RSHUTDOWN - Request shutdown */ + NULL, /* PHP_MINFO - Module info */ + PHP_VERSION, /* Version */ + PHP_MODULE_GLOBALS(random), /* ZTS Module globals */ + NULL, /* PHP_GINIT - Global initialization */ + NULL, /* PHP_GSHUTDOWN - Global shutdown */ + NULL, /* Post deactivate */ + STANDARD_MODULE_PROPERTIES_EX +}; +/* }}} */ + +#ifdef COMPILE_DL_RANDOM +# ifdef ZTS +ZEND_TSRMLS_CACHE_DEFINE() +# endif +ZEND_GET_MODULE(random) +#endif diff --git a/ext/random/random.stub.php b/ext/random/random.stub.php new file mode 100644 index 0000000000000..ef3a3b14b6171 --- /dev/null +++ b/ext/random/random.stub.php @@ -0,0 +1,137 @@ +ce_flags |= ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES; + zend_class_implements(class_entry, 1, class_entry_Random_Engine); + + return class_entry; +} + +static zend_class_entry *register_class_Random_Engine_PcgOneseq128XslRr64(zend_class_entry *class_entry_Random_Engine) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "Random\\Engine", "PcgOneseq128XslRr64", class_Random_Engine_PcgOneseq128XslRr64_methods); + class_entry = zend_register_internal_class_ex(&ce, NULL); + class_entry->ce_flags |= ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES; + zend_class_implements(class_entry, 1, class_entry_Random_Engine); + + return class_entry; +} + +static zend_class_entry *register_class_Random_Engine_Xoshiro256StarStar(zend_class_entry *class_entry_Random_Engine) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "Random\\Engine", "Xoshiro256StarStar", class_Random_Engine_Xoshiro256StarStar_methods); + class_entry = zend_register_internal_class_ex(&ce, NULL); + class_entry->ce_flags |= ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES; + zend_class_implements(class_entry, 1, class_entry_Random_Engine); + + return class_entry; +} + +static zend_class_entry *register_class_Random_Engine_Secure(zend_class_entry *class_entry_Random_CryptoSafeEngine) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "Random\\Engine", "Secure", class_Random_Engine_Secure_methods); + class_entry = zend_register_internal_class_ex(&ce, NULL); + class_entry->ce_flags |= ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE; + zend_class_implements(class_entry, 1, class_entry_Random_CryptoSafeEngine); + + return class_entry; +} + +static zend_class_entry *register_class_Random_Engine(void) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "Random", "Engine", class_Random_Engine_methods); + class_entry = zend_register_internal_interface(&ce); + + return class_entry; +} + +static zend_class_entry *register_class_Random_CryptoSafeEngine(zend_class_entry *class_entry_Random_Engine) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "Random", "CryptoSafeEngine", class_Random_CryptoSafeEngine_methods); + class_entry = zend_register_internal_interface(&ce); + zend_class_implements(class_entry, 1, class_entry_Random_Engine); + + return class_entry; +} + +static zend_class_entry *register_class_Random_Randomizer(void) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "Random", "Randomizer", class_Random_Randomizer_methods); + class_entry = zend_register_internal_class_ex(&ce, NULL); + class_entry->ce_flags |= ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES; + + zend_string *property_engine_class_Random_Engine = zend_string_init("Random\\Engine", sizeof("Random\\Engine")-1, 1); + zval property_engine_default_value; + ZVAL_UNDEF(&property_engine_default_value); + zend_string *property_engine_name = zend_string_init("engine", sizeof("engine") - 1, 1); + zend_declare_typed_property(class_entry, property_engine_name, &property_engine_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_engine_class_Random_Engine, 0, 0)); + zend_string_release(property_engine_name); + + return class_entry; +} diff --git a/ext/random/randomizer.c b/ext/random/randomizer.c new file mode 100644 index 0000000000000..25632b99e1d25 --- /dev/null +++ b/ext/random/randomizer.c @@ -0,0 +1,285 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Go Kudo | + +----------------------------------------------------------------------+ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "php.h" +#include "php_random.h" + +#include "ext/standard/php_array.h" +#include "ext/standard/php_string.h" + +#include "ext/spl/spl_exceptions.h" +#include "Zend/zend_exceptions.h" + +static inline void randomizer_common_init(php_random_randomizer *randomizer, zend_object *engine_object) { + if (engine_object->ce->type == ZEND_INTERNAL_CLASS) { + /* Internal classes always php_random_engine struct */ + php_random_engine *engine = php_random_engine_from_obj(engine_object); + + /* Copy engine pointers */ + randomizer->algo = engine->algo; + randomizer->status = engine->status; + } else { + /* Self allocation */ + randomizer->status = php_random_status_alloc(&php_random_algo_user, false); + php_random_status_state_user *state = randomizer->status->state; + zend_string *mname; + zend_function *generate_method; + + mname = zend_string_init("generate", strlen("generate"), 0); + generate_method = zend_hash_find_ptr(&engine_object->ce->function_table, mname); + zend_string_release(mname); + + /* Create compatible state */ + state->object = engine_object; + state->generate_method = generate_method; + + /* Copy common pointers */ + randomizer->algo = &php_random_algo_user; + + /* Mark self-allocated for memory management */ + randomizer->is_userland_algo = true; + } +} + +/* {{{ Random\Randomizer::__construct() */ +PHP_METHOD(Random_Randomizer, __construct) +{ + php_random_randomizer *randomizer = Z_RANDOM_RANDOMIZER_P(ZEND_THIS); + zend_object *engine_object = NULL; + zval zengine_object; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_OBJ_OF_CLASS_OR_NULL(engine_object, random_ce_Random_Engine); + ZEND_PARSE_PARAMETERS_END(); + + /* Create default RNG instance */ + if (!engine_object) { + engine_object = random_ce_Random_Engine_Secure->create_object(random_ce_Random_Engine_Secure); + + /* No need self-refcount */ + GC_DELREF(engine_object); + } + + ZVAL_OBJ(&zengine_object, engine_object); + + zend_update_property(random_ce_Random_Randomizer, Z_OBJ_P(ZEND_THIS), "engine", strlen("engine"), &zengine_object); + + randomizer_common_init(randomizer, engine_object); +} +/* }}} */ + +/* {{{ Generate random number in range */ +PHP_METHOD(Random_Randomizer, getInt) +{ + php_random_randomizer *randomizer = Z_RANDOM_RANDOMIZER_P(ZEND_THIS); + uint64_t result; + zend_long min, max; + int argc = ZEND_NUM_ARGS(); + + if (argc == 0) { + result = randomizer->algo->generate(randomizer->status); + if (randomizer->status->last_generated_size > sizeof(zend_long)) { + zend_throw_exception(spl_ce_RuntimeException, "Generated value exceeds size of int", 0); + RETURN_THROWS(); + } + if (randomizer->status->last_unsafe) { + zend_throw_exception(spl_ce_RuntimeException, "Random number generation failed", 0); + RETURN_THROWS(); + } + RETURN_LONG((zend_long) (result >> 1)); + } + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_LONG(min) + Z_PARAM_LONG(max) + ZEND_PARSE_PARAMETERS_END(); + + if (UNEXPECTED(max < min)) { + zend_argument_value_error(2, "must be greater than or equal to argument #1 ($min)"); + RETURN_THROWS(); + } + + result = randomizer->algo->range(randomizer->status, min, max); + if (randomizer->status->last_unsafe) { + zend_throw_exception(spl_ce_RuntimeException, "Random number generation failed", 0); + RETURN_THROWS(); + } + + RETURN_LONG((zend_long) result); +} +/* }}} */ + +/* {{{ Generate random bytes string in ordered length */ +PHP_METHOD(Random_Randomizer, getBytes) +{ + php_random_randomizer *randomizer = Z_RANDOM_RANDOMIZER_P(ZEND_THIS); + zend_string *retval; + zend_long length; + uint64_t result; + size_t total_size = 0, required_size; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(length) + ZEND_PARSE_PARAMETERS_END(); + + if (length < 1) { + zend_argument_value_error(1, "must be greater than 0"); + RETURN_THROWS(); + } + + retval = zend_string_alloc(length, 0); + required_size = length; + + while (total_size < required_size) { + result = randomizer->algo->generate(randomizer->status); + if (randomizer->status->last_unsafe) { + zend_string_free(retval); + zend_throw_exception(spl_ce_RuntimeException, "Random number generation failed", 0); + RETURN_THROWS(); + } + for (size_t i = 0; i < randomizer->status->last_generated_size; i++) { + ZSTR_VAL(retval)[total_size++] = (result >> (i * 8)) & 0xff; + if (total_size >= required_size) { + break; + } + } + } + + ZSTR_VAL(retval)[length] = '\0'; + RETURN_STR(retval); +} +/* }}} */ + +/* {{{ Shuffling array */ +PHP_METHOD(Random_Randomizer, shuffleArray) +{ + php_random_randomizer *randomizer = Z_RANDOM_RANDOMIZER_P(ZEND_THIS); + zval *array; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY(array) + ZEND_PARSE_PARAMETERS_END(); + + ZVAL_DUP(return_value, array); + if (!php_array_data_shuffle(randomizer->algo, randomizer->status, return_value)) { + zend_throw_exception(spl_ce_RuntimeException, "Random number generation failed", 0); + RETURN_THROWS(); + } +} +/* }}} */ + +/* {{{ Shuffling binary */ +PHP_METHOD(Random_Randomizer, shuffleBytes) +{ + php_random_randomizer *randomizer = Z_RANDOM_RANDOMIZER_P(ZEND_THIS); + zend_string *bytes; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STR(bytes) + ZEND_PARSE_PARAMETERS_END(); + + if (ZSTR_LEN(bytes) < 2) { + RETURN_STR_COPY(bytes); + } + + RETVAL_STRINGL(ZSTR_VAL(bytes), ZSTR_LEN(bytes)); + if (!php_binary_string_shuffle(randomizer->algo, randomizer->status, Z_STRVAL_P(return_value), (zend_long) Z_STRLEN_P(return_value))) { + zend_throw_exception(spl_ce_RuntimeException, "Random number generation failed", 0); + RETURN_THROWS(); + } +} +/* }}} */ + +/* {{{ Pick keys */ +PHP_METHOD(Random_Randomizer, pickArrayKeys) +{ + php_random_randomizer *randomizer = Z_RANDOM_RANDOMIZER_P(ZEND_THIS); + zval *input, t; + zend_long num_req; + + ZEND_PARSE_PARAMETERS_START(2, 2); + Z_PARAM_ARRAY(input) + Z_PARAM_LONG(num_req) + ZEND_PARSE_PARAMETERS_END(); + + if (!php_array_pick_keys( + randomizer->algo, + randomizer->status, + input, + num_req, + return_value, + false) + ) { + RETURN_THROWS(); + } + + /* Keep compatibility, But the result is always an array */ + if (Z_TYPE_P(return_value) != IS_ARRAY) { + ZVAL_COPY_VALUE(&t, return_value); + array_init(return_value); + zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &t); + } +} +/* }}} */ + +/* {{{ Random\Randomizer::__serialize() */ +PHP_METHOD(Random_Randomizer, __serialize) +{ + php_random_randomizer *randomizer = Z_RANDOM_RANDOMIZER_P(ZEND_THIS); + zval t; + + ZEND_PARSE_PARAMETERS_NONE(); + + array_init(return_value); + ZVAL_ARR(&t, zend_std_get_properties(&randomizer->std)); + Z_TRY_ADDREF(t); + zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &t); +} +/* }}} */ + +/* {{{ Random\Randomizer::__unserialize() */ +PHP_METHOD(Random_Randomizer, __unserialize) +{ + php_random_randomizer *randomizer = Z_RANDOM_RANDOMIZER_P(ZEND_THIS); + HashTable *d; + zval *members_zv; + zval *zengine; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY_HT(d); + ZEND_PARSE_PARAMETERS_END(); + + members_zv = zend_hash_index_find(d, 0); + if (!members_zv || Z_TYPE_P(members_zv) != IS_ARRAY) { + zend_throw_exception(NULL, "Incomplete or ill-formed serialization data", 0); + RETURN_THROWS(); + } + object_properties_load(&randomizer->std, Z_ARRVAL_P(members_zv)); + + zengine = zend_read_property(randomizer->std.ce, &randomizer->std, "engine", strlen("engine"), 0, NULL); + if (Z_TYPE_P(zengine) != IS_OBJECT || !instanceof_function(Z_OBJCE_P(zengine), random_ce_Random_Engine)) { + zend_throw_exception(NULL, "Incomplete or ill-formed serialization data", 0); + RETURN_THROWS(); + } + + randomizer_common_init(randomizer, Z_OBJ_P(zengine)); +} +/* }}} */ diff --git a/ext/standard/tests/general_functions/bug46587.phpt b/ext/random/tests/01_functions/bug46587.phpt similarity index 100% rename from ext/standard/tests/general_functions/bug46587.phpt rename to ext/random/tests/01_functions/bug46587.phpt diff --git a/ext/standard/tests/math/bug75170.phpt b/ext/random/tests/01_functions/bug75170.phpt similarity index 100% rename from ext/standard/tests/math/bug75170.phpt rename to ext/random/tests/01_functions/bug75170.phpt diff --git a/ext/standard/tests/math/bug75514.phpt b/ext/random/tests/01_functions/bug75514.phpt similarity index 100% rename from ext/standard/tests/math/bug75514.phpt rename to ext/random/tests/01_functions/bug75514.phpt diff --git a/ext/standard/tests/math/lcg_value_basic.phpt b/ext/random/tests/01_functions/lcg_value_basic.phpt similarity index 100% rename from ext/standard/tests/math/lcg_value_basic.phpt rename to ext/random/tests/01_functions/lcg_value_basic.phpt diff --git a/ext/standard/tests/math/mt_getrandmax_basic.phpt b/ext/random/tests/01_functions/mt_getrandmax_basic.phpt similarity index 100% rename from ext/standard/tests/math/mt_getrandmax_basic.phpt rename to ext/random/tests/01_functions/mt_getrandmax_basic.phpt diff --git a/ext/standard/tests/math/mt_rand_basic.phpt b/ext/random/tests/01_functions/mt_rand_basic.phpt similarity index 100% rename from ext/standard/tests/math/mt_rand_basic.phpt rename to ext/random/tests/01_functions/mt_rand_basic.phpt diff --git a/ext/standard/tests/math/mt_rand_value.phpt b/ext/random/tests/01_functions/mt_rand_value.phpt similarity index 100% rename from ext/standard/tests/math/mt_rand_value.phpt rename to ext/random/tests/01_functions/mt_rand_value.phpt diff --git a/ext/standard/tests/math/mt_srand_basic.phpt b/ext/random/tests/01_functions/mt_srand_basic.phpt similarity index 100% rename from ext/standard/tests/math/mt_srand_basic.phpt rename to ext/random/tests/01_functions/mt_srand_basic.phpt diff --git a/ext/standard/tests/math/rand_basic.phpt b/ext/random/tests/01_functions/rand_basic.phpt similarity index 100% rename from ext/standard/tests/math/rand_basic.phpt rename to ext/random/tests/01_functions/rand_basic.phpt diff --git a/ext/standard/tests/math/rand_inverted_order.phpt b/ext/random/tests/01_functions/rand_inverted_order.phpt similarity index 100% rename from ext/standard/tests/math/rand_inverted_order.phpt rename to ext/random/tests/01_functions/rand_inverted_order.phpt diff --git a/ext/standard/tests/random/random_bytes.phpt b/ext/random/tests/01_functions/random_bytes.phpt similarity index 100% rename from ext/standard/tests/random/random_bytes.phpt rename to ext/random/tests/01_functions/random_bytes.phpt diff --git a/ext/standard/tests/random/random_bytes_error.phpt b/ext/random/tests/01_functions/random_bytes_error.phpt similarity index 100% rename from ext/standard/tests/random/random_bytes_error.phpt rename to ext/random/tests/01_functions/random_bytes_error.phpt diff --git a/ext/standard/tests/random/random_int.phpt b/ext/random/tests/01_functions/random_int.phpt similarity index 100% rename from ext/standard/tests/random/random_int.phpt rename to ext/random/tests/01_functions/random_int.phpt diff --git a/ext/standard/tests/random/random_int_error.phpt b/ext/random/tests/01_functions/random_int_error.phpt similarity index 100% rename from ext/standard/tests/random/random_int_error.phpt rename to ext/random/tests/01_functions/random_int_error.phpt diff --git a/ext/standard/tests/random/reflection.phpt b/ext/random/tests/01_functions/reflection.phpt similarity index 100% rename from ext/standard/tests/random/reflection.phpt rename to ext/random/tests/01_functions/reflection.phpt diff --git a/ext/standard/tests/math/srand_basic.phpt b/ext/random/tests/01_functions/srand_basic.phpt similarity index 100% rename from ext/standard/tests/math/srand_basic.phpt rename to ext/random/tests/01_functions/srand_basic.phpt diff --git a/ext/random/tests/02_engine/all_serialize_error.phpt b/ext/random/tests/02_engine/all_serialize_error.phpt new file mode 100644 index 0000000000000..082b99e9529bd --- /dev/null +++ b/ext/random/tests/02_engine/all_serialize_error.phpt @@ -0,0 +1,77 @@ +--TEST-- +Random: Engine: serialize error pattern +--FILE-- +getMessage() . PHP_EOL; +} + +// invalid hex +try { + unserialize('O:21:"Random\Engine\Mt19937":2:{i:0;a:0:{}i:1;a:626:{i:0;s:8:"5aa6b98g";i:1;s:8:"8660cc14";i:2;s:8:"c0b631ca";i:3;s:8:"e85464ad";i:4;s:8:"70fa6108";i:5;s:8:"c5ed9c3c";i:6;s:8:"b05b7ff1";i:7;s:8:"faf33a3a";i:8;s:8:"ab7c0e61";i:9;s:8:"2d4c9c37";i:10;s:8:"daffe918";i:11;s:8:"644f25b9";i:12;s:8:"fdb352e5";i:13;s:8:"434dafa9";i:14;s:8:"a3c9826e";i:15;s:8:"923dfd8c";i:16;s:8:"f641a225";i:17;s:8:"d87134b3";i:18;s:8:"c3b7926e";i:19;s:8:"c880b60d";i:20;s:8:"975f362d";i:21;s:8:"48192b77";i:22;s:8:"6f7dd08b";i:23;s:8:"7d4ad4f8";i:24;s:8:"d805b910";i:25;s:8:"8c98b365";i:26;s:8:"89e54af9";i:27;s:8:"e5257a3c";i:28;s:8:"8f596624";i:29;s:8:"3f42f88a";i:30;s:8:"6a7e95cc";i:31;s:8:"e1349e4d";i:32;s:8:"4539bc92";i:33;s:8:"045a3148";i:34;s:8:"c27cf7b9";i:35;s:8:"c64e8009";i:36;s:8:"dc5ed02e";i:37;s:8:"5753c741";i:38;s:8:"50be0a82";i:39;s:8:"822da0ee";i:40;s:8:"42fdb3c6";i:41;s:8:"c668fc80";i:42;s:8:"262376c6";i:43;s:8:"17b998c6";i:44;s:8:"1f3aac02";i:45;s:8:"6f939c7e";i:46;s:8:"21c099f4";i:47;s:8:"0f4b5c76";i:48;s:8:"64799ac0";i:49;s:8:"45d3bb99";i:50;s:8:"8ff3eb79";i:51;s:8:"61996264";i:52;s:8:"2b9ab1f4";i:53;s:8:"a0d0c50f";i:54;s:8:"f08713ce";i:55;s:8:"6b1cf9d8";i:56;s:8:"52d92cc5";i:57;s:8:"34bcec6f";i:58;s:8:"83ffa063";i:59;s:8:"a002321d";i:60;s:8:"386c46d9";i:61;s:8:"45e2c63e";i:62;s:8:"f481bdf4";i:63;s:8:"df58facb";i:64;s:8:"1781e49b";i:65;s:8:"1d968a6b";i:66;s:8:"8aa7fdd2";i:67;s:8:"631ac8cd";i:68;s:8:"8090ff8e";i:69;s:8:"4ddd4a5d";i:70;s:8:"ff6d8193";i:71;s:8:"39e18244";i:72;s:8:"4efe15db";i:73;s:8:"3fe64cd5";i:74;s:8:"e693a97f";i:75;s:8:"807a34e9";i:76;s:8:"6dc16ae7";i:77;s:8:"a1e1ed1d";i:78;s:8:"605bdc86";i:79;s:8:"abb1830e";i:80;s:8:"2baabeda";i:81;s:8:"fc0f66ad";i:82;s:8:"9cf4fb96";i:83;s:8:"b50ff764";i:84;s:8:"67c37a3d";i:85;s:8:"15253035";i:86;s:8:"387fce47";i:87;s:8:"5c3299a5";i:88;s:8:"125602b9";i:89;s:8:"daeb445c";i:90;s:8:"289e5a8d";i:91;s:8:"b47cd48c";i:92;s:8:"8664db23";i:93;s:8:"b9352af8";i:94;s:8:"51684d88";i:95;s:8:"b204cbc9";i:96;s:8:"e1b6becc";i:97;s:8:"dbbe0ee6";i:98;s:8:"89e54027";i:99;s:8:"a6512564";i:100;s:8:"3aab8d17";i:101;s:8:"7cab5272";i:102;s:8:"1804e981";i:103;s:8:"0bfebe2c";i:104;s:8:"7c082a4c";i:105;s:8:"06bc80c3";i:106;s:8:"c63db839";i:107;s:8:"59edb59d";i:108;s:8:"b52d9655";i:109;s:8:"efce041b";i:110;s:8:"57c1f890";i:111;s:8:"15499697";i:112;s:8:"e0ad3e3a";i:113;s:8:"408d4c16";i:114;s:8:"e53cf468";i:115;s:8:"6ea3d84f";i:116;s:8:"0a2bc4f2";i:117;s:8:"27b59259";i:118;s:8:"d223514b";i:119;s:8:"434c4185";i:120;s:8:"999c71df";i:121;s:8:"46134706";i:122;s:8:"f7f048d0";i:123;s:8:"7f5cfe34";i:124;s:8:"9bdc475c";i:125;s:8:"c1cf42ea";i:126;s:8:"65be324b";i:127;s:8:"68c7a1dc";i:128;s:8:"a7c8c5a2";i:129;s:8:"ecc4cd8a";i:130;s:8:"a761615c";i:131;s:8:"718ec534";i:132;s:8:"c8b086fc";i:133;s:8:"ead759f9";i:134;s:8:"eb9df4a0";i:135;s:8:"ec5b25f9";i:136;s:8:"3a6ff94e";i:137;s:8:"4511a3de";i:138;s:8:"9034f1c6";i:139;s:8:"5c1c6d14";i:140;s:8:"a228ed46";i:141;s:8:"ffe19f2f";i:142;s:8:"f21f68cf";i:143;s:8:"6c2235a4";i:144;s:8:"e0aed51a";i:145;s:8:"2ade527b";i:146;s:8:"65f3c758";i:147;s:8:"f6e868b7";i:148;s:8:"49b770a5";i:149;s:8:"48ab9158";i:150;s:8:"ab07a671";i:151;s:8:"4ef74251";i:152;s:8:"f4cad644";i:153;s:8:"1576c59e";i:154;s:8:"3882bbd1";i:155;s:8:"e49f32c0";i:156;s:8:"c67b757c";i:157;s:8:"3528c5d8";i:158;s:8:"371c3e34";i:159;s:8:"7f1d614f";i:160;s:8:"b9e19e66";i:161;s:8:"80a1d97f";i:162;s:8:"7f93c9f3";i:163;s:8:"4e82ea48";i:164;s:8:"9675e170";i:165;s:8:"aa54caef";i:166;s:8:"3bb9838e";i:167;s:8:"9c0d0a2b";i:168;s:8:"2595d91f";i:169;s:8:"84cc7ff2";i:170;s:8:"9457c4ee";i:171;s:8:"405b6bc8";i:172;s:8:"5aa668a4";i:173;s:8:"e94dfca2";i:174;s:8:"89c0d739";i:175;s:8:"459f8eb3";i:176;s:8:"76b95b42";i:177;s:8:"0e5ceafb";i:178;s:8:"55d4eaee";i:179;s:8:"a55a4784";i:180;s:8:"8c23e133";i:181;s:8:"0994f794";i:182;s:8:"a8d4d1b7";i:183;s:8:"0a50b177";i:184;s:8:"65409f44";i:185;s:8:"acf34e81";i:186;s:8:"e32f278e";i:187;s:8:"7aa21660";i:188;s:8:"9da66881";i:189;s:8:"5c4df7c5";i:190;s:8:"b21f8a4e";i:191;s:8:"d2cc6756";i:192;s:8:"8716f97d";i:193;s:8:"aabd84c5";i:194;s:8:"2e0a965a";i:195;s:8:"b4acc29c";i:196;s:8:"ed3be992";i:197;s:8:"867d9400";i:198;s:8:"a696ba3e";i:199;s:8:"8e2af3d9";i:200;s:8:"8fd95ea9";i:201;s:8:"930903d8";i:202;s:8:"4508dbb1";i:203;s:8:"80598d21";i:204;s:8:"df87fb74";i:205;s:8:"9d019d24";i:206;s:8:"05d5ce2e";i:207;s:8:"ed69bcfe";i:208;s:8:"f83a8d70";i:209;s:8:"750b10bd";i:210;s:8:"c0df892c";i:211;s:8:"df41f215";i:212;s:8:"03df46e3";i:213;s:8:"4e6dce66";i:214;s:8:"ea45a428";i:215;s:8:"cbbf3ff6";i:216;s:8:"f931c7b2";i:217;s:8:"80d19eab";i:218;s:8:"0e2c13da";i:219;s:8:"4b99ee8b";i:220;s:8:"2311d69e";i:221;s:8:"ca9050a7";i:222;s:8:"a4d9eec1";i:223;s:8:"ee665c77";i:224;s:8:"0714f961";i:225;s:8:"81f5be46";i:226;s:8:"420ea4bf";i:227;s:8:"281c28f0";i:228;s:8:"9936ab3f";i:229;s:8:"e4ca8936";i:230;s:8:"224d247b";i:231;s:8:"a564dffe";i:232;s:8:"d15045d2";i:233;s:8:"97d01e8f";i:234;s:8:"71793232";i:235;s:8:"43fe461e";i:236;s:8:"3b50916b";i:237;s:8:"ff242439";i:238;s:8:"a748a0a9";i:239;s:8:"ff51a2a6";i:240;s:8:"dec0ad0d";i:241;s:8:"4bcf2a2e";i:242;s:8:"2be6a97f";i:243;s:8:"eb3e636a";i:244;s:8:"81e31d64";i:245;s:8:"6ca4db9d";i:246;s:8:"bc5bd290";i:247;s:8:"ed2d2ba4";i:248;s:8:"00c46d55";i:249;s:8:"27578407";i:250;s:8:"bedbbcd8";i:251;s:8:"18172abc";i:252;s:8:"eaa5b9cf";i:253;s:8:"9e87ab84";i:254;s:8:"a0e2741c";i:255;s:8:"3bb931ae";i:256;s:8:"3a7b75cd";i:257;s:8:"1052cbf5";i:258;s:8:"b7afd060";i:259;s:8:"1d143d51";i:260;s:8:"17fb7422";i:261;s:8:"523fd346";i:262;s:8:"50429afd";i:263;s:8:"308ece7b";i:264;s:8:"285aa5c1";i:265;s:8:"d8afd736";i:266;s:8:"2e30512e";i:267;s:8:"af78ce36";i:268;s:8:"5967e738";i:269;s:8:"e19795de";i:270;s:8:"6d416e39";i:271;s:8:"23570261";i:272;s:8:"23106924";i:273;s:8:"a4b9add9";i:274;s:8:"baa83e79";i:275;s:8:"97ca48d3";i:276;s:8:"f369a004";i:277;s:8:"fa521bf6";i:278;s:8:"312e9e5e";i:279;s:8:"3acdb850";i:280;s:8:"485cbad7";i:281;s:8:"ddc2d365";i:282;s:8:"d8b87c13";i:283;s:8:"51f6aa7a";i:284;s:8:"b1c231ce";i:285;s:8:"926b9e57";i:286;s:8:"32bd5241";i:287;s:8:"b72cf0a0";i:288;s:8:"0cd99f51";i:289;s:8:"80815c0e";i:290;s:8:"a3459115";i:291;s:8:"5bb05439";i:292;s:8:"ee67a282";i:293;s:8:"65e533c5";i:294;s:8:"62e57b7a";i:295;s:8:"0f4a42fe";i:296;s:8:"ff5b8045";i:297;s:8:"cb9d60cf";i:298;s:8:"ffc891cd";i:299;s:8:"514d6027";i:300;s:8:"5b92df3b";i:301;s:8:"1fb47b1a";i:302;s:8:"a2382db8";i:303;s:8:"6f8b23cd";i:304;s:8:"db72737a";i:305;s:8:"d2d5022b";i:306;s:8:"eef762a7";i:307;s:8:"771fc2ed";i:308;s:8:"6581afb1";i:309;s:8:"64d7ffbd";i:310;s:8:"93a15a65";i:311;s:8:"fdc8b81b";i:312;s:8:"6369357b";i:313;s:8:"a8a58d2e";i:314;s:8:"d9ab2ed6";i:315;s:8:"0465340d";i:316;s:8:"eed4e3f1";i:317;s:8:"7e15ef9d";i:318;s:8:"99444e57";i:319;s:8:"959a6881";i:320;s:8:"e7498e10";i:321;s:8:"d7d0699e";i:322;s:8:"12372566";i:323;s:8:"b9f97c89";i:324;s:8:"5e688172";i:325;s:8:"8105e3b8";i:326;s:8:"627e9227";i:327;s:8:"67edb47d";i:328;s:8:"74e69d5f";i:329;s:8:"b8c5df2c";i:330;s:8:"14f06e80";i:331;s:8:"1a0af27f";i:332;s:8:"fa6ad68b";i:333;s:8:"d5938b0d";i:334;s:8:"2df9b8cc";i:335;s:8:"d90bd16c";i:336;s:8:"18779d24";i:337;s:8:"e1b81866";i:338;s:8:"dc51e106";i:339;s:8:"5078c0ce";i:340;s:8:"8fde3be3";i:341;s:8:"85939b08";i:342;s:8:"50f5565d";i:343;s:8:"281ec5f0";i:344;s:8:"ee2c4dfb";i:345;s:8:"89df2863";i:346;s:8:"0444ef85";i:347;s:8:"d52671b6";i:348;s:8:"84695c5d";i:349;s:8:"ceb19fc9";i:350;s:8:"8cd19595";i:351;s:8:"9773504b";i:352;s:8:"72031351";i:353;s:8:"285de56c";i:354;s:8:"08d949a1";i:355;s:8:"d1d24d60";i:356;s:8:"41c57eff";i:357;s:8:"ded22dfc";i:358;s:8:"9f799eb2";i:359;s:8:"ac14c164";i:360;s:8:"23ab3dac";i:361;s:8:"76de10e0";i:362;s:8:"72c2ac0b";i:363;s:8:"cce93127";i:364;s:8:"32bbb7e5";i:365;s:8:"b8af671a";i:366;s:8:"24dfc65a";i:367;s:8:"d7c8431f";i:368;s:8:"5cf3dffd";i:369;s:8:"7a93645c";i:370;s:8:"36bb66f8";i:371;s:8:"dc055709";i:372;s:8:"93c58efc";i:373;s:8:"11b9a1e6";i:374;s:8:"e487f36b";i:375;s:8:"7c3bcb46";i:376;s:8:"e4d724a3";i:377;s:8:"b732d653";i:378;s:8:"afc9c219";i:379;s:8:"ea658830";i:380;s:8:"b9052af6";i:381;s:8:"aa46a473";i:382;s:8:"1b37a35e";i:383;s:8:"0ba14ee9";i:384;s:8:"c7e150ac";i:385;s:8:"055ffa96";i:386;s:8:"e2bf88fb";i:387;s:8:"88f1f936";i:388;s:8:"488c7bdb";i:389;s:8:"a9dacbd1";i:390;s:8:"aaeea26f";i:391;s:8:"237af1cb";i:392;s:8:"e3dbdb50";i:393;s:8:"c8e5949e";i:394;s:8:"e70a989a";i:395;s:8:"fa540d6d";i:396;s:8:"f055b2f2";i:397;s:8:"1fc8acd4";i:398;s:8:"accb1a9b";i:399;s:8:"bedfb89c";i:400;s:8:"3d243ec0";i:401;s:8:"d2f3f2d8";i:402;s:8:"d83e7c53";i:403;s:8:"4f12e12d";i:404;s:8:"f48f33d1";i:405;s:8:"9555d943";i:406;s:8:"db853a55";i:407;s:8:"506a95b4";i:408;s:8:"408155d1";i:409;s:8:"32a384a0";i:410;s:8:"21c34eb4";i:411;s:8:"93e2e1ae";i:412;s:8:"88582a10";i:413;s:8:"1f065648";i:414;s:8:"96c3c81b";i:415;s:8:"412bc358";i:416;s:8:"11a4e990";i:417;s:8:"805ead73";i:418;s:8:"ec8ca56e";i:419;s:8:"a2ed4bdc";i:420;s:8:"ac408a10";i:421;s:8:"4ca98f96";i:422;s:8:"0686b6df";i:423;s:8:"9e9f7ae4";i:424;s:8:"07d5f658";i:425;s:8:"1c121a0c";i:426;s:8:"c11c8e82";i:427;s:8:"13f2c6d7";i:428;s:8:"e9f6ad9c";i:429;s:8:"bc1dc285";i:430;s:8:"e271fe4b";i:431;s:8:"34114ee5";i:432;s:8:"92ad1c39";i:433;s:8:"5ab5504b";i:434;s:8:"4f7118a7";i:435;s:8:"d1ceec2f";i:436;s:8:"adfd7622";i:437;s:8:"f4f4a9c0";i:438;s:8:"2d4f21e3";i:439;s:8:"3b4bed9e";i:440;s:8:"42ac5810";i:441;s:8:"7eae8db5";i:442;s:8:"09dc329e";i:443;s:8:"3c723314";i:444;s:8:"7fb50c08";i:445;s:8:"71fd69dd";i:446;s:8:"013ce542";i:447;s:8:"0add0d73";i:448;s:8:"465d495a";i:449;s:8:"9f8ddb9d";i:450;s:8:"f293e79e";i:451;s:8:"d6f59d72";i:452;s:8:"ac22e38f";i:453;s:8:"e96d5751";i:454;s:8:"fba79717";i:455;s:8:"39fedf2f";i:456;s:8:"3fb25196";i:457;s:8:"fcdaa825";i:458;s:8:"9a960022";i:459;s:8:"5371af3d";i:460;s:8:"df7faf0a";i:461;s:8:"82c22c85";i:462;s:8:"dfbbae9f";i:463;s:8:"403a4b84";i:464;s:8:"bc938282";i:465;s:8:"d2355fbc";i:466;s:8:"8d72b179";i:467;s:8:"dced02b2";i:468;s:8:"227b82e3";i:469;s:8:"24c60db6";i:470;s:8:"45092b73";i:471;s:8:"767c0e1d";i:472;s:8:"7eb5c592";i:473;s:8:"d0b356d9";i:474;s:8:"dc95ee45";i:475;s:8:"39aa5820";i:476;s:8:"9e6e1868";i:477;s:8:"ffe72d78";i:478;s:8:"82ae0503";i:479;s:8:"2ea981ad";i:480;s:8:"6935faba";i:481;s:8:"69c2ba98";i:482;s:8:"69dd219f";i:483;s:8:"860e0d0d";i:484;s:8:"5f451aa3";i:485;s:8:"a9d5f07d";i:486;s:8:"ec623682";i:487;s:8:"04d8adba";i:488;s:8:"717e8c76";i:489;s:8:"f9c1fb75";i:490;s:8:"ab4c9e06";i:491;s:8:"3895ef4d";i:492;s:8:"37c09060";i:493;s:8:"f3b7c652";i:494;s:8:"74dc421e";i:495;s:8:"97d458be";i:496;s:8:"1a1ddea1";i:497;s:8:"001c68ad";i:498;s:8:"680f033f";i:499;s:8:"b2e9bd38";i:500;s:8:"54192958";i:501;s:8:"b6dd0693";i:502;s:8:"9d149906";i:503;s:8:"9ba5479a";i:504;s:8:"18ba25e8";i:505;s:8:"16fa3e51";i:506;s:8:"1e74b698";i:507;s:8:"c5932028";i:508;s:8:"5a6cf6cd";i:509;s:8:"6fa90e13";i:510;s:8:"bcabdecb";i:511;s:8:"ae23af9a";i:512;s:8:"76b297eb";i:513;s:8:"d1332632";i:514;s:8:"693a55c3";i:515;s:8:"7c4beb9b";i:516;s:8:"409a0a15";i:517;s:8:"60f68335";i:518;s:8:"febcb934";i:519;s:8:"f32c6f8c";i:520;s:8:"aacacf0d";i:521;s:8:"d1f56e99";i:522;s:8:"d65bcf00";i:523;s:8:"361c9633";i:524;s:8:"cd34d7f4";i:525;s:8:"37d38e62";i:526;s:8:"942dee9d";i:527;s:8:"f41c9445";i:528;s:8:"7e9a8b5d";i:529;s:8:"4b941ed7";i:530;s:8:"c6256dce";i:531;s:8:"6c285146";i:532;s:8:"5920147e";i:533;s:8:"934d59ed";i:534;s:8:"5f400a1d";i:535;s:8:"1a5de58c";i:536;s:8:"11e601de";i:537;s:8:"cf4da287";i:538;s:8:"756a54e4";i:539;s:8:"9f2b1015";i:540;s:8:"3a3df642";i:541;s:8:"5d024d64";i:542;s:8:"2d617393";i:543;s:8:"e1b4ec53";i:544;s:8:"24996b5e";i:545;s:8:"7a271bf1";i:546;s:8:"68d7da60";i:547;s:8:"06d3bc73";i:548;s:8:"f21095f7";i:549;s:8:"93e6bfd9";i:550;s:8:"e80b015b";i:551;s:8:"4ddd4ede";i:552;s:8:"cec3d813";i:553;s:8:"281f2a65";i:554;s:8:"ffa068fb";i:555;s:8:"60e712e7";i:556;s:8:"4abcecac";i:557;s:8:"eef01060";i:558;s:8:"8e79e897";i:559;s:8:"b0cd39fe";i:560;s:8:"28cc9604";i:561;s:8:"c7bd58e0";i:562;s:8:"c73b1765";i:563;s:8:"e87d5e07";i:564;s:8:"03b7710d";i:565;s:8:"5f64c5dc";i:566;s:8:"4ae7b6f1";i:567;s:8:"401b9876";i:568;s:8:"9a162522";i:569;s:8:"2a1d6390";i:570;s:8:"c115eeff";i:571;s:8:"e549f7df";i:572;s:8:"a159c579";i:573;s:8:"48a44ff6";i:574;s:8:"e65fd1fc";i:575;s:8:"bb74cd7b";i:576;s:8:"87cc0383";i:577;s:8:"052755ee";i:578;s:8:"eba29b21";i:579;s:8:"88e2efa9";i:580;s:8:"a1a521b9";i:581;s:8:"ffb6f27b";i:582;s:8:"4fea3248";i:583;s:8:"0f46ad03";i:584;s:8:"58902f76";i:585;s:8:"cfb3f87e";i:586;s:8:"efb3d85c";i:587;s:8:"1e0f4648";i:588;s:8:"f95eda47";i:589;s:8:"c5220b0b";i:590;s:8:"e6574ef8";i:591;s:8:"f34b162e";i:592;s:8:"09b08b14";i:593;s:8:"dae53353";i:594;s:8:"ccc600dd";i:595;s:8:"b3ae8f4f";i:596;s:8:"daed379c";i:597;s:8:"113f5973";i:598;s:8:"6a66bb0a";i:599;s:8:"697817f3";i:600;s:8:"00b78167";i:601;s:8:"1e7c2ed9";i:602;s:8:"65f8a3c5";i:603;s:8:"c50f5c14";i:604;s:8:"a1665ca7";i:605;s:8:"d31c5017";i:606;s:8:"2e421193";i:607;s:8:"cb8d29a9";i:608;s:8:"fa9723b5";i:609;s:8:"8a4d18e4";i:610;s:8:"88081a19";i:611;s:8:"242623e4";i:612;s:8:"86202155";i:613;s:8:"ab55982f";i:614;s:8:"f1a3c261";i:615;s:8:"c44eaa91";i:616;s:8:"06c4716f";i:617;s:8:"60a25216";i:618;s:8:"dcce86a3";i:619;s:8:"bfcec0cd";i:620;s:8:"fb4170ed";i:621;s:8:"9bf5e563";i:622;s:8:"04fa51af";i:623;s:8:"dd86eeb1";i:624;i:0;i:625;i:0;}}'); +} catch (\Exception $e) { + echo $e->getMessage() . PHP_EOL; +} + +// invalid count +try { + unserialize('O:21:"Random\Engine\Mt19937":2:{i:0;a:0:{}i:1;a:626:{i:0;s:8:"5aa6b986";i:1;s:8:"8660cc14";i:2;s:8:"c0b631ca";i:3;s:8:"e85464ad";i:4;s:8:"70fa6108";i:5;s:8:"c5ed9c3c";i:6;s:8:"b05b7ff1";i:7;s:8:"faf33a3a";i:8;s:8:"ab7c0e61";i:9;s:8:"2d4c9c37";i:10;s:8:"daffe918";i:11;s:8:"644f25b9";i:12;s:8:"fdb352e5";i:13;s:8:"434dafa9";i:14;s:8:"a3c9826e";i:15;s:8:"923dfd8c";i:16;s:8:"f641a225";i:17;s:8:"d87134b3";i:18;s:8:"c3b7926e";i:19;s:8:"c880b60d";i:20;s:8:"975f362d";i:21;s:8:"48192b77";i:22;s:8:"6f7dd08b";i:23;s:8:"7d4ad4f8";i:24;s:8:"d805b910";i:25;s:8:"8c98b365";i:26;s:8:"89e54af9";i:27;s:8:"e5257a3c";i:28;s:8:"8f596624";i:29;s:8:"3f42f88a";i:30;s:8:"6a7e95cc";i:31;s:8:"e1349e4d";i:32;s:8:"4539bc92";i:33;s:8:"045a3148";i:34;s:8:"c27cf7b9";i:35;s:8:"c64e8009";i:36;s:8:"dc5ed02e";i:37;s:8:"5753c741";i:38;s:8:"50be0a82";i:39;s:8:"822da0ee";i:40;s:8:"42fdb3c6";i:41;s:8:"c668fc80";i:42;s:8:"262376c6";i:43;s:8:"17b998c6";i:44;s:8:"1f3aac02";i:45;s:8:"6f939c7e";i:46;s:8:"21c099f4";i:47;s:8:"0f4b5c76";i:48;s:8:"64799ac0";i:49;s:8:"45d3bb99";i:50;s:8:"8ff3eb79";i:51;s:8:"61996264";i:52;s:8:"2b9ab1f4";i:53;s:8:"a0d0c50f";i:54;s:8:"f08713ce";i:55;s:8:"6b1cf9d8";i:56;s:8:"52d92cc5";i:57;s:8:"34bcec6f";i:58;s:8:"83ffa063";i:59;s:8:"a002321d";i:60;s:8:"386c46d9";i:61;s:8:"45e2c63e";i:62;s:8:"f481bdf4";i:63;s:8:"df58facb";i:64;s:8:"1781e49b";i:65;s:8:"1d968a6b";i:66;s:8:"8aa7fdd2";i:67;s:8:"631ac8cd";i:68;s:8:"8090ff8e";i:69;s:8:"4ddd4a5d";i:70;s:8:"ff6d8193";i:71;s:8:"39e18244";i:72;s:8:"4efe15db";i:73;s:8:"3fe64cd5";i:74;s:8:"e693a97f";i:75;s:8:"807a34e9";i:76;s:8:"6dc16ae7";i:77;s:8:"a1e1ed1d";i:78;s:8:"605bdc86";i:79;s:8:"abb1830e";i:80;s:8:"2baabeda";i:81;s:8:"fc0f66ad";i:82;s:8:"9cf4fb96";i:83;s:8:"b50ff764";i:84;s:8:"67c37a3d";i:85;s:8:"15253035";i:86;s:8:"387fce47";i:87;s:8:"5c3299a5";i:88;s:8:"125602b9";i:89;s:8:"daeb445c";i:90;s:8:"289e5a8d";i:91;s:8:"b47cd48c";i:92;s:8:"8664db23";i:93;s:8:"b9352af8";i:94;s:8:"51684d88";i:95;s:8:"b204cbc9";i:96;s:8:"e1b6becc";i:97;s:8:"dbbe0ee6";i:98;s:8:"89e54027";i:99;s:8:"a6512564";i:100;s:8:"3aab8d17";i:101;s:8:"7cab5272";i:102;s:8:"1804e981";i:103;s:8:"0bfebe2c";i:104;s:8:"7c082a4c";i:105;s:8:"06bc80c3";i:106;s:8:"c63db839";i:107;s:8:"59edb59d";i:108;s:8:"b52d9655";i:109;s:8:"efce041b";i:110;s:8:"57c1f890";i:111;s:8:"15499697";i:112;s:8:"e0ad3e3a";i:113;s:8:"408d4c16";i:114;s:8:"e53cf468";i:115;s:8:"6ea3d84f";i:116;s:8:"0a2bc4f2";i:117;s:8:"27b59259";i:118;s:8:"d223514b";i:119;s:8:"434c4185";i:120;s:8:"999c71df";i:121;s:8:"46134706";i:122;s:8:"f7f048d0";i:123;s:8:"7f5cfe34";i:124;s:8:"9bdc475c";i:125;s:8:"c1cf42ea";i:126;s:8:"65be324b";i:127;s:8:"68c7a1dc";i:128;s:8:"a7c8c5a2";i:129;s:8:"ecc4cd8a";i:130;s:8:"a761615c";i:131;s:8:"718ec534";i:132;s:8:"c8b086fc";i:133;s:8:"ead759f9";i:134;s:8:"eb9df4a0";i:135;s:8:"ec5b25f9";i:136;s:8:"3a6ff94e";i:137;s:8:"4511a3de";i:138;s:8:"9034f1c6";i:139;s:8:"5c1c6d14";i:140;s:8:"a228ed46";i:141;s:8:"ffe19f2f";i:142;s:8:"f21f68cf";i:143;s:8:"6c2235a4";i:144;s:8:"e0aed51a";i:145;s:8:"2ade527b";i:146;s:8:"65f3c758";i:147;s:8:"f6e868b7";i:148;s:8:"49b770a5";i:149;s:8:"48ab9158";i:150;s:8:"ab07a671";i:151;s:8:"4ef74251";i:152;s:8:"f4cad644";i:153;s:8:"1576c59e";i:154;s:8:"3882bbd1";i:155;s:8:"e49f32c0";i:156;s:8:"c67b757c";i:157;s:8:"3528c5d8";i:158;s:8:"371c3e34";i:159;s:8:"7f1d614f";i:160;s:8:"b9e19e66";i:161;s:8:"80a1d97f";i:162;s:8:"7f93c9f3";i:163;s:8:"4e82ea48";i:164;s:8:"9675e170";i:165;s:8:"aa54caef";i:166;s:8:"3bb9838e";i:167;s:8:"9c0d0a2b";i:168;s:8:"2595d91f";i:169;s:8:"84cc7ff2";i:170;s:8:"9457c4ee";i:171;s:8:"405b6bc8";i:172;s:8:"5aa668a4";i:173;s:8:"e94dfca2";i:174;s:8:"89c0d739";i:175;s:8:"459f8eb3";i:176;s:8:"76b95b42";i:177;s:8:"0e5ceafb";i:178;s:8:"55d4eaee";i:179;s:8:"a55a4784";i:180;s:8:"8c23e133";i:181;s:8:"0994f794";i:182;s:8:"a8d4d1b7";i:183;s:8:"0a50b177";i:184;s:8:"65409f44";i:185;s:8:"acf34e81";i:186;s:8:"e32f278e";i:187;s:8:"7aa21660";i:188;s:8:"9da66881";i:189;s:8:"5c4df7c5";i:190;s:8:"b21f8a4e";i:191;s:8:"d2cc6756";i:192;s:8:"8716f97d";i:193;s:8:"aabd84c5";i:194;s:8:"2e0a965a";i:195;s:8:"b4acc29c";i:196;s:8:"ed3be992";i:197;s:8:"867d9400";i:198;s:8:"a696ba3e";i:199;s:8:"8e2af3d9";i:200;s:8:"8fd95ea9";i:201;s:8:"930903d8";i:202;s:8:"4508dbb1";i:203;s:8:"80598d21";i:204;s:8:"df87fb74";i:205;s:8:"9d019d24";i:206;s:8:"05d5ce2e";i:207;s:8:"ed69bcfe";i:208;s:8:"f83a8d70";i:209;s:8:"750b10bd";i:210;s:8:"c0df892c";i:211;s:8:"df41f215";i:212;s:8:"03df46e3";i:213;s:8:"4e6dce66";i:214;s:8:"ea45a428";i:215;s:8:"cbbf3ff6";i:216;s:8:"f931c7b2";i:217;s:8:"80d19eab";i:218;s:8:"0e2c13da";i:219;s:8:"4b99ee8b";i:220;s:8:"2311d69e";i:221;s:8:"ca9050a7";i:222;s:8:"a4d9eec1";i:223;s:8:"ee665c77";i:224;s:8:"0714f961";i:225;s:8:"81f5be46";i:226;s:8:"420ea4bf";i:227;s:8:"281c28f0";i:228;s:8:"9936ab3f";i:229;s:8:"e4ca8936";i:230;s:8:"224d247b";i:231;s:8:"a564dffe";i:232;s:8:"d15045d2";i:233;s:8:"97d01e8f";i:234;s:8:"71793232";i:235;s:8:"43fe461e";i:236;s:8:"3b50916b";i:237;s:8:"ff242439";i:238;s:8:"a748a0a9";i:239;s:8:"ff51a2a6";i:240;s:8:"dec0ad0d";i:241;s:8:"4bcf2a2e";i:242;s:8:"2be6a97f";i:243;s:8:"eb3e636a";i:244;s:8:"81e31d64";i:245;s:8:"6ca4db9d";i:246;s:8:"bc5bd290";i:247;s:8:"ed2d2ba4";i:248;s:8:"00c46d55";i:249;s:8:"27578407";i:250;s:8:"bedbbcd8";i:251;s:8:"18172abc";i:252;s:8:"eaa5b9cf";i:253;s:8:"9e87ab84";i:254;s:8:"a0e2741c";i:255;s:8:"3bb931ae";i:256;s:8:"3a7b75cd";i:257;s:8:"1052cbf5";i:258;s:8:"b7afd060";i:259;s:8:"1d143d51";i:260;s:8:"17fb7422";i:261;s:8:"523fd346";i:262;s:8:"50429afd";i:263;s:8:"308ece7b";i:264;s:8:"285aa5c1";i:265;s:8:"d8afd736";i:266;s:8:"2e30512e";i:267;s:8:"af78ce36";i:268;s:8:"5967e738";i:269;s:8:"e19795de";i:270;s:8:"6d416e39";i:271;s:8:"23570261";i:272;s:8:"23106924";i:273;s:8:"a4b9add9";i:274;s:8:"baa83e79";i:275;s:8:"97ca48d3";i:276;s:8:"f369a004";i:277;s:8:"fa521bf6";i:278;s:8:"312e9e5e";i:279;s:8:"3acdb850";i:280;s:8:"485cbad7";i:281;s:8:"ddc2d365";i:282;s:8:"d8b87c13";i:283;s:8:"51f6aa7a";i:284;s:8:"b1c231ce";i:285;s:8:"926b9e57";i:286;s:8:"32bd5241";i:287;s:8:"b72cf0a0";i:288;s:8:"0cd99f51";i:289;s:8:"80815c0e";i:290;s:8:"a3459115";i:291;s:8:"5bb05439";i:292;s:8:"ee67a282";i:293;s:8:"65e533c5";i:294;s:8:"62e57b7a";i:295;s:8:"0f4a42fe";i:296;s:8:"ff5b8045";i:297;s:8:"cb9d60cf";i:298;s:8:"ffc891cd";i:299;s:8:"514d6027";i:300;s:8:"5b92df3b";i:301;s:8:"1fb47b1a";i:302;s:8:"a2382db8";i:303;s:8:"6f8b23cd";i:304;s:8:"db72737a";i:305;s:8:"d2d5022b";i:306;s:8:"eef762a7";i:307;s:8:"771fc2ed";i:308;s:8:"6581afb1";i:309;s:8:"64d7ffbd";i:310;s:8:"93a15a65";i:311;s:8:"fdc8b81b";i:312;s:8:"6369357b";i:313;s:8:"a8a58d2e";i:314;s:8:"d9ab2ed6";i:315;s:8:"0465340d";i:316;s:8:"eed4e3f1";i:317;s:8:"7e15ef9d";i:318;s:8:"99444e57";i:319;s:8:"959a6881";i:320;s:8:"e7498e10";i:321;s:8:"d7d0699e";i:322;s:8:"12372566";i:323;s:8:"b9f97c89";i:324;s:8:"5e688172";i:325;s:8:"8105e3b8";i:326;s:8:"627e9227";i:327;s:8:"67edb47d";i:328;s:8:"74e69d5f";i:329;s:8:"b8c5df2c";i:330;s:8:"14f06e80";i:331;s:8:"1a0af27f";i:332;s:8:"fa6ad68b";i:333;s:8:"d5938b0d";i:334;s:8:"2df9b8cc";i:335;s:8:"d90bd16c";i:336;s:8:"18779d24";i:337;s:8:"e1b81866";i:338;s:8:"dc51e106";i:339;s:8:"5078c0ce";i:340;s:8:"8fde3be3";i:341;s:8:"85939b08";i:342;s:8:"50f5565d";i:343;s:8:"281ec5f0";i:344;s:8:"ee2c4dfb";i:345;s:8:"89df2863";i:346;s:8:"0444ef85";i:347;s:8:"d52671b6";i:348;s:8:"84695c5d";i:349;s:8:"ceb19fc9";i:350;s:8:"8cd19595";i:351;s:8:"9773504b";i:352;s:8:"72031351";i:353;s:8:"285de56c";i:354;s:8:"08d949a1";i:355;s:8:"d1d24d60";i:356;s:8:"41c57eff";i:357;s:8:"ded22dfc";i:358;s:8:"9f799eb2";i:359;s:8:"ac14c164";i:360;s:8:"23ab3dac";i:361;s:8:"76de10e0";i:362;s:8:"72c2ac0b";i:363;s:8:"cce93127";i:364;s:8:"32bbb7e5";i:365;s:8:"b8af671a";i:366;s:8:"24dfc65a";i:367;s:8:"d7c8431f";i:368;s:8:"5cf3dffd";i:369;s:8:"7a93645c";i:370;s:8:"36bb66f8";i:371;s:8:"dc055709";i:372;s:8:"93c58efc";i:373;s:8:"11b9a1e6";i:374;s:8:"e487f36b";i:375;s:8:"7c3bcb46";i:376;s:8:"e4d724a3";i:377;s:8:"b732d653";i:378;s:8:"afc9c219";i:379;s:8:"ea658830";i:380;s:8:"b9052af6";i:381;s:8:"aa46a473";i:382;s:8:"1b37a35e";i:383;s:8:"0ba14ee9";i:384;s:8:"c7e150ac";i:385;s:8:"055ffa96";i:386;s:8:"e2bf88fb";i:387;s:8:"88f1f936";i:388;s:8:"488c7bdb";i:389;s:8:"a9dacbd1";i:390;s:8:"aaeea26f";i:391;s:8:"237af1cb";i:392;s:8:"e3dbdb50";i:393;s:8:"c8e5949e";i:394;s:8:"e70a989a";i:395;s:8:"fa540d6d";i:396;s:8:"f055b2f2";i:397;s:8:"1fc8acd4";i:398;s:8:"accb1a9b";i:399;s:8:"bedfb89c";i:400;s:8:"3d243ec0";i:401;s:8:"d2f3f2d8";i:402;s:8:"d83e7c53";i:403;s:8:"4f12e12d";i:404;s:8:"f48f33d1";i:405;s:8:"9555d943";i:406;s:8:"db853a55";i:407;s:8:"506a95b4";i:408;s:8:"408155d1";i:409;s:8:"32a384a0";i:410;s:8:"21c34eb4";i:411;s:8:"93e2e1ae";i:412;s:8:"88582a10";i:413;s:8:"1f065648";i:414;s:8:"96c3c81b";i:415;s:8:"412bc358";i:416;s:8:"11a4e990";i:417;s:8:"805ead73";i:418;s:8:"ec8ca56e";i:419;s:8:"a2ed4bdc";i:420;s:8:"ac408a10";i:421;s:8:"4ca98f96";i:422;s:8:"0686b6df";i:423;s:8:"9e9f7ae4";i:424;s:8:"07d5f658";i:425;s:8:"1c121a0c";i:426;s:8:"c11c8e82";i:427;s:8:"13f2c6d7";i:428;s:8:"e9f6ad9c";i:429;s:8:"bc1dc285";i:430;s:8:"e271fe4b";i:431;s:8:"34114ee5";i:432;s:8:"92ad1c39";i:433;s:8:"5ab5504b";i:434;s:8:"4f7118a7";i:435;s:8:"d1ceec2f";i:436;s:8:"adfd7622";i:437;s:8:"f4f4a9c0";i:438;s:8:"2d4f21e3";i:439;s:8:"3b4bed9e";i:440;s:8:"42ac5810";i:441;s:8:"7eae8db5";i:442;s:8:"09dc329e";i:443;s:8:"3c723314";i:444;s:8:"7fb50c08";i:445;s:8:"71fd69dd";i:446;s:8:"013ce542";i:447;s:8:"0add0d73";i:448;s:8:"465d495a";i:449;s:8:"9f8ddb9d";i:450;s:8:"f293e79e";i:451;s:8:"d6f59d72";i:452;s:8:"ac22e38f";i:453;s:8:"e96d5751";i:454;s:8:"fba79717";i:455;s:8:"39fedf2f";i:456;s:8:"3fb25196";i:457;s:8:"fcdaa825";i:458;s:8:"9a960022";i:459;s:8:"5371af3d";i:460;s:8:"df7faf0a";i:461;s:8:"82c22c85";i:462;s:8:"dfbbae9f";i:463;s:8:"403a4b84";i:464;s:8:"bc938282";i:465;s:8:"d2355fbc";i:466;s:8:"8d72b179";i:467;s:8:"dced02b2";i:468;s:8:"227b82e3";i:469;s:8:"24c60db6";i:470;s:8:"45092b73";i:471;s:8:"767c0e1d";i:472;s:8:"7eb5c592";i:473;s:8:"d0b356d9";i:474;s:8:"dc95ee45";i:475;s:8:"39aa5820";i:476;s:8:"9e6e1868";i:477;s:8:"ffe72d78";i:478;s:8:"82ae0503";i:479;s:8:"2ea981ad";i:480;s:8:"6935faba";i:481;s:8:"69c2ba98";i:482;s:8:"69dd219f";i:483;s:8:"860e0d0d";i:484;s:8:"5f451aa3";i:485;s:8:"a9d5f07d";i:486;s:8:"ec623682";i:487;s:8:"04d8adba";i:488;s:8:"717e8c76";i:489;s:8:"f9c1fb75";i:490;s:8:"ab4c9e06";i:491;s:8:"3895ef4d";i:492;s:8:"37c09060";i:493;s:8:"f3b7c652";i:494;s:8:"74dc421e";i:495;s:8:"97d458be";i:496;s:8:"1a1ddea1";i:497;s:8:"001c68ad";i:498;s:8:"680f033f";i:499;s:8:"b2e9bd38";i:500;s:8:"54192958";i:501;s:8:"b6dd0693";i:502;s:8:"9d149906";i:503;s:8:"9ba5479a";i:504;s:8:"18ba25e8";i:505;s:8:"16fa3e51";i:506;s:8:"1e74b698";i:507;s:8:"c5932028";i:508;s:8:"5a6cf6cd";i:509;s:8:"6fa90e13";i:510;s:8:"bcabdecb";i:511;s:8:"ae23af9a";i:512;s:8:"76b297eb";i:513;s:8:"d1332632";i:514;s:8:"693a55c3";i:515;s:8:"7c4beb9b";i:516;s:8:"409a0a15";i:517;s:8:"60f68335";i:518;s:8:"febcb934";i:519;s:8:"f32c6f8c";i:520;s:8:"aacacf0d";i:521;s:8:"d1f56e99";i:522;s:8:"d65bcf00";i:523;s:8:"361c9633";i:524;s:8:"cd34d7f4";i:525;s:8:"37d38e62";i:526;s:8:"942dee9d";i:527;s:8:"f41c9445";i:528;s:8:"7e9a8b5d";i:529;s:8:"4b941ed7";i:530;s:8:"c6256dce";i:531;s:8:"6c285146";i:532;s:8:"5920147e";i:533;s:8:"934d59ed";i:534;s:8:"5f400a1d";i:535;s:8:"1a5de58c";i:536;s:8:"11e601de";i:537;s:8:"cf4da287";i:538;s:8:"756a54e4";i:539;s:8:"9f2b1015";i:540;s:8:"3a3df642";i:541;s:8:"5d024d64";i:542;s:8:"2d617393";i:543;s:8:"e1b4ec53";i:544;s:8:"24996b5e";i:545;s:8:"7a271bf1";i:546;s:8:"68d7da60";i:547;s:8:"06d3bc73";i:548;s:8:"f21095f7";i:549;s:8:"93e6bfd9";i:550;s:8:"e80b015b";i:551;s:8:"4ddd4ede";i:552;s:8:"cec3d813";i:553;s:8:"281f2a65";i:554;s:8:"ffa068fb";i:555;s:8:"60e712e7";i:556;s:8:"4abcecac";i:557;s:8:"eef01060";i:558;s:8:"8e79e897";i:559;s:8:"b0cd39fe";i:560;s:8:"28cc9604";i:561;s:8:"c7bd58e0";i:562;s:8:"c73b1765";i:563;s:8:"e87d5e07";i:564;s:8:"03b7710d";i:565;s:8:"5f64c5dc";i:566;s:8:"4ae7b6f1";i:567;s:8:"401b9876";i:568;s:8:"9a162522";i:569;s:8:"2a1d6390";i:570;s:8:"c115eeff";i:571;s:8:"e549f7df";i:572;s:8:"a159c579";i:573;s:8:"48a44ff6";i:574;s:8:"e65fd1fc";i:575;s:8:"bb74cd7b";i:576;s:8:"87cc0383";i:577;s:8:"052755ee";i:578;s:8:"eba29b21";i:579;s:8:"88e2efa9";i:580;s:8:"a1a521b9";i:581;s:8:"ffb6f27b";i:582;s:8:"4fea3248";i:583;s:8:"0f46ad03";i:584;s:8:"58902f76";i:585;s:8:"cfb3f87e";i:586;s:8:"efb3d85c";i:587;s:8:"1e0f4648";i:588;s:8:"f95eda47";i:589;s:8:"c5220b0b";i:590;s:8:"e6574ef8";i:591;s:8:"f34b162e";i:592;s:8:"09b08b14";i:593;s:8:"dae53353";i:594;s:8:"ccc600dd";i:595;s:8:"b3ae8f4f";i:596;s:8:"daed379c";i:597;s:8:"113f5973";i:598;s:8:"6a66bb0a";i:599;s:8:"697817f3";i:600;s:8:"00b78167";i:601;s:8:"1e7c2ed9";i:602;s:8:"65f8a3c5";i:603;s:8:"c50f5c14";i:604;s:8:"a1665ca7";i:605;s:8:"d31c5017";i:606;s:8:"2e421193";i:607;s:8:"cb8d29a9";i:608;s:8:"fa9723b5";i:609;s:8:"8a4d18e4";i:610;s:8:"88081a19";i:611;s:8:"242623e4";i:612;s:8:"86202155";i:613;s:8:"ab55982f";i:614;s:8:"f1a3c261";i:615;s:8:"c44eaa91";i:616;s:8:"06c4716f";i:617;s:8:"60a25216";i:618;s:8:"dcce86a3";i:619;s:8:"bfcec0cd";i:620;s:8:"fb4170ed";i:621;s:8:"9bf5e563";i:622;s:8:"04fa51af";i:623;s:8:"dd86eeb1";i:624;i:1000;i:625;i:0;}}'); +} catch (\Exception $e) { + echo $e->getMessage() . PHP_EOL; +} + +// invalid mode +try { + unserialize('O:21:"Random\Engine\Mt19937":2:{i:0;a:0:{}i:1;a:626:{i:0;s:8:"5aa6b986";i:1;s:8:"8660cc14";i:2;s:8:"c0b631ca";i:3;s:8:"e85464ad";i:4;s:8:"70fa6108";i:5;s:8:"c5ed9c3c";i:6;s:8:"b05b7ff1";i:7;s:8:"faf33a3a";i:8;s:8:"ab7c0e61";i:9;s:8:"2d4c9c37";i:10;s:8:"daffe918";i:11;s:8:"644f25b9";i:12;s:8:"fdb352e5";i:13;s:8:"434dafa9";i:14;s:8:"a3c9826e";i:15;s:8:"923dfd8c";i:16;s:8:"f641a225";i:17;s:8:"d87134b3";i:18;s:8:"c3b7926e";i:19;s:8:"c880b60d";i:20;s:8:"975f362d";i:21;s:8:"48192b77";i:22;s:8:"6f7dd08b";i:23;s:8:"7d4ad4f8";i:24;s:8:"d805b910";i:25;s:8:"8c98b365";i:26;s:8:"89e54af9";i:27;s:8:"e5257a3c";i:28;s:8:"8f596624";i:29;s:8:"3f42f88a";i:30;s:8:"6a7e95cc";i:31;s:8:"e1349e4d";i:32;s:8:"4539bc92";i:33;s:8:"045a3148";i:34;s:8:"c27cf7b9";i:35;s:8:"c64e8009";i:36;s:8:"dc5ed02e";i:37;s:8:"5753c741";i:38;s:8:"50be0a82";i:39;s:8:"822da0ee";i:40;s:8:"42fdb3c6";i:41;s:8:"c668fc80";i:42;s:8:"262376c6";i:43;s:8:"17b998c6";i:44;s:8:"1f3aac02";i:45;s:8:"6f939c7e";i:46;s:8:"21c099f4";i:47;s:8:"0f4b5c76";i:48;s:8:"64799ac0";i:49;s:8:"45d3bb99";i:50;s:8:"8ff3eb79";i:51;s:8:"61996264";i:52;s:8:"2b9ab1f4";i:53;s:8:"a0d0c50f";i:54;s:8:"f08713ce";i:55;s:8:"6b1cf9d8";i:56;s:8:"52d92cc5";i:57;s:8:"34bcec6f";i:58;s:8:"83ffa063";i:59;s:8:"a002321d";i:60;s:8:"386c46d9";i:61;s:8:"45e2c63e";i:62;s:8:"f481bdf4";i:63;s:8:"df58facb";i:64;s:8:"1781e49b";i:65;s:8:"1d968a6b";i:66;s:8:"8aa7fdd2";i:67;s:8:"631ac8cd";i:68;s:8:"8090ff8e";i:69;s:8:"4ddd4a5d";i:70;s:8:"ff6d8193";i:71;s:8:"39e18244";i:72;s:8:"4efe15db";i:73;s:8:"3fe64cd5";i:74;s:8:"e693a97f";i:75;s:8:"807a34e9";i:76;s:8:"6dc16ae7";i:77;s:8:"a1e1ed1d";i:78;s:8:"605bdc86";i:79;s:8:"abb1830e";i:80;s:8:"2baabeda";i:81;s:8:"fc0f66ad";i:82;s:8:"9cf4fb96";i:83;s:8:"b50ff764";i:84;s:8:"67c37a3d";i:85;s:8:"15253035";i:86;s:8:"387fce47";i:87;s:8:"5c3299a5";i:88;s:8:"125602b9";i:89;s:8:"daeb445c";i:90;s:8:"289e5a8d";i:91;s:8:"b47cd48c";i:92;s:8:"8664db23";i:93;s:8:"b9352af8";i:94;s:8:"51684d88";i:95;s:8:"b204cbc9";i:96;s:8:"e1b6becc";i:97;s:8:"dbbe0ee6";i:98;s:8:"89e54027";i:99;s:8:"a6512564";i:100;s:8:"3aab8d17";i:101;s:8:"7cab5272";i:102;s:8:"1804e981";i:103;s:8:"0bfebe2c";i:104;s:8:"7c082a4c";i:105;s:8:"06bc80c3";i:106;s:8:"c63db839";i:107;s:8:"59edb59d";i:108;s:8:"b52d9655";i:109;s:8:"efce041b";i:110;s:8:"57c1f890";i:111;s:8:"15499697";i:112;s:8:"e0ad3e3a";i:113;s:8:"408d4c16";i:114;s:8:"e53cf468";i:115;s:8:"6ea3d84f";i:116;s:8:"0a2bc4f2";i:117;s:8:"27b59259";i:118;s:8:"d223514b";i:119;s:8:"434c4185";i:120;s:8:"999c71df";i:121;s:8:"46134706";i:122;s:8:"f7f048d0";i:123;s:8:"7f5cfe34";i:124;s:8:"9bdc475c";i:125;s:8:"c1cf42ea";i:126;s:8:"65be324b";i:127;s:8:"68c7a1dc";i:128;s:8:"a7c8c5a2";i:129;s:8:"ecc4cd8a";i:130;s:8:"a761615c";i:131;s:8:"718ec534";i:132;s:8:"c8b086fc";i:133;s:8:"ead759f9";i:134;s:8:"eb9df4a0";i:135;s:8:"ec5b25f9";i:136;s:8:"3a6ff94e";i:137;s:8:"4511a3de";i:138;s:8:"9034f1c6";i:139;s:8:"5c1c6d14";i:140;s:8:"a228ed46";i:141;s:8:"ffe19f2f";i:142;s:8:"f21f68cf";i:143;s:8:"6c2235a4";i:144;s:8:"e0aed51a";i:145;s:8:"2ade527b";i:146;s:8:"65f3c758";i:147;s:8:"f6e868b7";i:148;s:8:"49b770a5";i:149;s:8:"48ab9158";i:150;s:8:"ab07a671";i:151;s:8:"4ef74251";i:152;s:8:"f4cad644";i:153;s:8:"1576c59e";i:154;s:8:"3882bbd1";i:155;s:8:"e49f32c0";i:156;s:8:"c67b757c";i:157;s:8:"3528c5d8";i:158;s:8:"371c3e34";i:159;s:8:"7f1d614f";i:160;s:8:"b9e19e66";i:161;s:8:"80a1d97f";i:162;s:8:"7f93c9f3";i:163;s:8:"4e82ea48";i:164;s:8:"9675e170";i:165;s:8:"aa54caef";i:166;s:8:"3bb9838e";i:167;s:8:"9c0d0a2b";i:168;s:8:"2595d91f";i:169;s:8:"84cc7ff2";i:170;s:8:"9457c4ee";i:171;s:8:"405b6bc8";i:172;s:8:"5aa668a4";i:173;s:8:"e94dfca2";i:174;s:8:"89c0d739";i:175;s:8:"459f8eb3";i:176;s:8:"76b95b42";i:177;s:8:"0e5ceafb";i:178;s:8:"55d4eaee";i:179;s:8:"a55a4784";i:180;s:8:"8c23e133";i:181;s:8:"0994f794";i:182;s:8:"a8d4d1b7";i:183;s:8:"0a50b177";i:184;s:8:"65409f44";i:185;s:8:"acf34e81";i:186;s:8:"e32f278e";i:187;s:8:"7aa21660";i:188;s:8:"9da66881";i:189;s:8:"5c4df7c5";i:190;s:8:"b21f8a4e";i:191;s:8:"d2cc6756";i:192;s:8:"8716f97d";i:193;s:8:"aabd84c5";i:194;s:8:"2e0a965a";i:195;s:8:"b4acc29c";i:196;s:8:"ed3be992";i:197;s:8:"867d9400";i:198;s:8:"a696ba3e";i:199;s:8:"8e2af3d9";i:200;s:8:"8fd95ea9";i:201;s:8:"930903d8";i:202;s:8:"4508dbb1";i:203;s:8:"80598d21";i:204;s:8:"df87fb74";i:205;s:8:"9d019d24";i:206;s:8:"05d5ce2e";i:207;s:8:"ed69bcfe";i:208;s:8:"f83a8d70";i:209;s:8:"750b10bd";i:210;s:8:"c0df892c";i:211;s:8:"df41f215";i:212;s:8:"03df46e3";i:213;s:8:"4e6dce66";i:214;s:8:"ea45a428";i:215;s:8:"cbbf3ff6";i:216;s:8:"f931c7b2";i:217;s:8:"80d19eab";i:218;s:8:"0e2c13da";i:219;s:8:"4b99ee8b";i:220;s:8:"2311d69e";i:221;s:8:"ca9050a7";i:222;s:8:"a4d9eec1";i:223;s:8:"ee665c77";i:224;s:8:"0714f961";i:225;s:8:"81f5be46";i:226;s:8:"420ea4bf";i:227;s:8:"281c28f0";i:228;s:8:"9936ab3f";i:229;s:8:"e4ca8936";i:230;s:8:"224d247b";i:231;s:8:"a564dffe";i:232;s:8:"d15045d2";i:233;s:8:"97d01e8f";i:234;s:8:"71793232";i:235;s:8:"43fe461e";i:236;s:8:"3b50916b";i:237;s:8:"ff242439";i:238;s:8:"a748a0a9";i:239;s:8:"ff51a2a6";i:240;s:8:"dec0ad0d";i:241;s:8:"4bcf2a2e";i:242;s:8:"2be6a97f";i:243;s:8:"eb3e636a";i:244;s:8:"81e31d64";i:245;s:8:"6ca4db9d";i:246;s:8:"bc5bd290";i:247;s:8:"ed2d2ba4";i:248;s:8:"00c46d55";i:249;s:8:"27578407";i:250;s:8:"bedbbcd8";i:251;s:8:"18172abc";i:252;s:8:"eaa5b9cf";i:253;s:8:"9e87ab84";i:254;s:8:"a0e2741c";i:255;s:8:"3bb931ae";i:256;s:8:"3a7b75cd";i:257;s:8:"1052cbf5";i:258;s:8:"b7afd060";i:259;s:8:"1d143d51";i:260;s:8:"17fb7422";i:261;s:8:"523fd346";i:262;s:8:"50429afd";i:263;s:8:"308ece7b";i:264;s:8:"285aa5c1";i:265;s:8:"d8afd736";i:266;s:8:"2e30512e";i:267;s:8:"af78ce36";i:268;s:8:"5967e738";i:269;s:8:"e19795de";i:270;s:8:"6d416e39";i:271;s:8:"23570261";i:272;s:8:"23106924";i:273;s:8:"a4b9add9";i:274;s:8:"baa83e79";i:275;s:8:"97ca48d3";i:276;s:8:"f369a004";i:277;s:8:"fa521bf6";i:278;s:8:"312e9e5e";i:279;s:8:"3acdb850";i:280;s:8:"485cbad7";i:281;s:8:"ddc2d365";i:282;s:8:"d8b87c13";i:283;s:8:"51f6aa7a";i:284;s:8:"b1c231ce";i:285;s:8:"926b9e57";i:286;s:8:"32bd5241";i:287;s:8:"b72cf0a0";i:288;s:8:"0cd99f51";i:289;s:8:"80815c0e";i:290;s:8:"a3459115";i:291;s:8:"5bb05439";i:292;s:8:"ee67a282";i:293;s:8:"65e533c5";i:294;s:8:"62e57b7a";i:295;s:8:"0f4a42fe";i:296;s:8:"ff5b8045";i:297;s:8:"cb9d60cf";i:298;s:8:"ffc891cd";i:299;s:8:"514d6027";i:300;s:8:"5b92df3b";i:301;s:8:"1fb47b1a";i:302;s:8:"a2382db8";i:303;s:8:"6f8b23cd";i:304;s:8:"db72737a";i:305;s:8:"d2d5022b";i:306;s:8:"eef762a7";i:307;s:8:"771fc2ed";i:308;s:8:"6581afb1";i:309;s:8:"64d7ffbd";i:310;s:8:"93a15a65";i:311;s:8:"fdc8b81b";i:312;s:8:"6369357b";i:313;s:8:"a8a58d2e";i:314;s:8:"d9ab2ed6";i:315;s:8:"0465340d";i:316;s:8:"eed4e3f1";i:317;s:8:"7e15ef9d";i:318;s:8:"99444e57";i:319;s:8:"959a6881";i:320;s:8:"e7498e10";i:321;s:8:"d7d0699e";i:322;s:8:"12372566";i:323;s:8:"b9f97c89";i:324;s:8:"5e688172";i:325;s:8:"8105e3b8";i:326;s:8:"627e9227";i:327;s:8:"67edb47d";i:328;s:8:"74e69d5f";i:329;s:8:"b8c5df2c";i:330;s:8:"14f06e80";i:331;s:8:"1a0af27f";i:332;s:8:"fa6ad68b";i:333;s:8:"d5938b0d";i:334;s:8:"2df9b8cc";i:335;s:8:"d90bd16c";i:336;s:8:"18779d24";i:337;s:8:"e1b81866";i:338;s:8:"dc51e106";i:339;s:8:"5078c0ce";i:340;s:8:"8fde3be3";i:341;s:8:"85939b08";i:342;s:8:"50f5565d";i:343;s:8:"281ec5f0";i:344;s:8:"ee2c4dfb";i:345;s:8:"89df2863";i:346;s:8:"0444ef85";i:347;s:8:"d52671b6";i:348;s:8:"84695c5d";i:349;s:8:"ceb19fc9";i:350;s:8:"8cd19595";i:351;s:8:"9773504b";i:352;s:8:"72031351";i:353;s:8:"285de56c";i:354;s:8:"08d949a1";i:355;s:8:"d1d24d60";i:356;s:8:"41c57eff";i:357;s:8:"ded22dfc";i:358;s:8:"9f799eb2";i:359;s:8:"ac14c164";i:360;s:8:"23ab3dac";i:361;s:8:"76de10e0";i:362;s:8:"72c2ac0b";i:363;s:8:"cce93127";i:364;s:8:"32bbb7e5";i:365;s:8:"b8af671a";i:366;s:8:"24dfc65a";i:367;s:8:"d7c8431f";i:368;s:8:"5cf3dffd";i:369;s:8:"7a93645c";i:370;s:8:"36bb66f8";i:371;s:8:"dc055709";i:372;s:8:"93c58efc";i:373;s:8:"11b9a1e6";i:374;s:8:"e487f36b";i:375;s:8:"7c3bcb46";i:376;s:8:"e4d724a3";i:377;s:8:"b732d653";i:378;s:8:"afc9c219";i:379;s:8:"ea658830";i:380;s:8:"b9052af6";i:381;s:8:"aa46a473";i:382;s:8:"1b37a35e";i:383;s:8:"0ba14ee9";i:384;s:8:"c7e150ac";i:385;s:8:"055ffa96";i:386;s:8:"e2bf88fb";i:387;s:8:"88f1f936";i:388;s:8:"488c7bdb";i:389;s:8:"a9dacbd1";i:390;s:8:"aaeea26f";i:391;s:8:"237af1cb";i:392;s:8:"e3dbdb50";i:393;s:8:"c8e5949e";i:394;s:8:"e70a989a";i:395;s:8:"fa540d6d";i:396;s:8:"f055b2f2";i:397;s:8:"1fc8acd4";i:398;s:8:"accb1a9b";i:399;s:8:"bedfb89c";i:400;s:8:"3d243ec0";i:401;s:8:"d2f3f2d8";i:402;s:8:"d83e7c53";i:403;s:8:"4f12e12d";i:404;s:8:"f48f33d1";i:405;s:8:"9555d943";i:406;s:8:"db853a55";i:407;s:8:"506a95b4";i:408;s:8:"408155d1";i:409;s:8:"32a384a0";i:410;s:8:"21c34eb4";i:411;s:8:"93e2e1ae";i:412;s:8:"88582a10";i:413;s:8:"1f065648";i:414;s:8:"96c3c81b";i:415;s:8:"412bc358";i:416;s:8:"11a4e990";i:417;s:8:"805ead73";i:418;s:8:"ec8ca56e";i:419;s:8:"a2ed4bdc";i:420;s:8:"ac408a10";i:421;s:8:"4ca98f96";i:422;s:8:"0686b6df";i:423;s:8:"9e9f7ae4";i:424;s:8:"07d5f658";i:425;s:8:"1c121a0c";i:426;s:8:"c11c8e82";i:427;s:8:"13f2c6d7";i:428;s:8:"e9f6ad9c";i:429;s:8:"bc1dc285";i:430;s:8:"e271fe4b";i:431;s:8:"34114ee5";i:432;s:8:"92ad1c39";i:433;s:8:"5ab5504b";i:434;s:8:"4f7118a7";i:435;s:8:"d1ceec2f";i:436;s:8:"adfd7622";i:437;s:8:"f4f4a9c0";i:438;s:8:"2d4f21e3";i:439;s:8:"3b4bed9e";i:440;s:8:"42ac5810";i:441;s:8:"7eae8db5";i:442;s:8:"09dc329e";i:443;s:8:"3c723314";i:444;s:8:"7fb50c08";i:445;s:8:"71fd69dd";i:446;s:8:"013ce542";i:447;s:8:"0add0d73";i:448;s:8:"465d495a";i:449;s:8:"9f8ddb9d";i:450;s:8:"f293e79e";i:451;s:8:"d6f59d72";i:452;s:8:"ac22e38f";i:453;s:8:"e96d5751";i:454;s:8:"fba79717";i:455;s:8:"39fedf2f";i:456;s:8:"3fb25196";i:457;s:8:"fcdaa825";i:458;s:8:"9a960022";i:459;s:8:"5371af3d";i:460;s:8:"df7faf0a";i:461;s:8:"82c22c85";i:462;s:8:"dfbbae9f";i:463;s:8:"403a4b84";i:464;s:8:"bc938282";i:465;s:8:"d2355fbc";i:466;s:8:"8d72b179";i:467;s:8:"dced02b2";i:468;s:8:"227b82e3";i:469;s:8:"24c60db6";i:470;s:8:"45092b73";i:471;s:8:"767c0e1d";i:472;s:8:"7eb5c592";i:473;s:8:"d0b356d9";i:474;s:8:"dc95ee45";i:475;s:8:"39aa5820";i:476;s:8:"9e6e1868";i:477;s:8:"ffe72d78";i:478;s:8:"82ae0503";i:479;s:8:"2ea981ad";i:480;s:8:"6935faba";i:481;s:8:"69c2ba98";i:482;s:8:"69dd219f";i:483;s:8:"860e0d0d";i:484;s:8:"5f451aa3";i:485;s:8:"a9d5f07d";i:486;s:8:"ec623682";i:487;s:8:"04d8adba";i:488;s:8:"717e8c76";i:489;s:8:"f9c1fb75";i:490;s:8:"ab4c9e06";i:491;s:8:"3895ef4d";i:492;s:8:"37c09060";i:493;s:8:"f3b7c652";i:494;s:8:"74dc421e";i:495;s:8:"97d458be";i:496;s:8:"1a1ddea1";i:497;s:8:"001c68ad";i:498;s:8:"680f033f";i:499;s:8:"b2e9bd38";i:500;s:8:"54192958";i:501;s:8:"b6dd0693";i:502;s:8:"9d149906";i:503;s:8:"9ba5479a";i:504;s:8:"18ba25e8";i:505;s:8:"16fa3e51";i:506;s:8:"1e74b698";i:507;s:8:"c5932028";i:508;s:8:"5a6cf6cd";i:509;s:8:"6fa90e13";i:510;s:8:"bcabdecb";i:511;s:8:"ae23af9a";i:512;s:8:"76b297eb";i:513;s:8:"d1332632";i:514;s:8:"693a55c3";i:515;s:8:"7c4beb9b";i:516;s:8:"409a0a15";i:517;s:8:"60f68335";i:518;s:8:"febcb934";i:519;s:8:"f32c6f8c";i:520;s:8:"aacacf0d";i:521;s:8:"d1f56e99";i:522;s:8:"d65bcf00";i:523;s:8:"361c9633";i:524;s:8:"cd34d7f4";i:525;s:8:"37d38e62";i:526;s:8:"942dee9d";i:527;s:8:"f41c9445";i:528;s:8:"7e9a8b5d";i:529;s:8:"4b941ed7";i:530;s:8:"c6256dce";i:531;s:8:"6c285146";i:532;s:8:"5920147e";i:533;s:8:"934d59ed";i:534;s:8:"5f400a1d";i:535;s:8:"1a5de58c";i:536;s:8:"11e601de";i:537;s:8:"cf4da287";i:538;s:8:"756a54e4";i:539;s:8:"9f2b1015";i:540;s:8:"3a3df642";i:541;s:8:"5d024d64";i:542;s:8:"2d617393";i:543;s:8:"e1b4ec53";i:544;s:8:"24996b5e";i:545;s:8:"7a271bf1";i:546;s:8:"68d7da60";i:547;s:8:"06d3bc73";i:548;s:8:"f21095f7";i:549;s:8:"93e6bfd9";i:550;s:8:"e80b015b";i:551;s:8:"4ddd4ede";i:552;s:8:"cec3d813";i:553;s:8:"281f2a65";i:554;s:8:"ffa068fb";i:555;s:8:"60e712e7";i:556;s:8:"4abcecac";i:557;s:8:"eef01060";i:558;s:8:"8e79e897";i:559;s:8:"b0cd39fe";i:560;s:8:"28cc9604";i:561;s:8:"c7bd58e0";i:562;s:8:"c73b1765";i:563;s:8:"e87d5e07";i:564;s:8:"03b7710d";i:565;s:8:"5f64c5dc";i:566;s:8:"4ae7b6f1";i:567;s:8:"401b9876";i:568;s:8:"9a162522";i:569;s:8:"2a1d6390";i:570;s:8:"c115eeff";i:571;s:8:"e549f7df";i:572;s:8:"a159c579";i:573;s:8:"48a44ff6";i:574;s:8:"e65fd1fc";i:575;s:8:"bb74cd7b";i:576;s:8:"87cc0383";i:577;s:8:"052755ee";i:578;s:8:"eba29b21";i:579;s:8:"88e2efa9";i:580;s:8:"a1a521b9";i:581;s:8:"ffb6f27b";i:582;s:8:"4fea3248";i:583;s:8:"0f46ad03";i:584;s:8:"58902f76";i:585;s:8:"cfb3f87e";i:586;s:8:"efb3d85c";i:587;s:8:"1e0f4648";i:588;s:8:"f95eda47";i:589;s:8:"c5220b0b";i:590;s:8:"e6574ef8";i:591;s:8:"f34b162e";i:592;s:8:"09b08b14";i:593;s:8:"dae53353";i:594;s:8:"ccc600dd";i:595;s:8:"b3ae8f4f";i:596;s:8:"daed379c";i:597;s:8:"113f5973";i:598;s:8:"6a66bb0a";i:599;s:8:"697817f3";i:600;s:8:"00b78167";i:601;s:8:"1e7c2ed9";i:602;s:8:"65f8a3c5";i:603;s:8:"c50f5c14";i:604;s:8:"a1665ca7";i:605;s:8:"d31c5017";i:606;s:8:"2e421193";i:607;s:8:"cb8d29a9";i:608;s:8:"fa9723b5";i:609;s:8:"8a4d18e4";i:610;s:8:"88081a19";i:611;s:8:"242623e4";i:612;s:8:"86202155";i:613;s:8:"ab55982f";i:614;s:8:"f1a3c261";i:615;s:8:"c44eaa91";i:616;s:8:"06c4716f";i:617;s:8:"60a25216";i:618;s:8:"dcce86a3";i:619;s:8:"bfcec0cd";i:620;s:8:"fb4170ed";i:621;s:8:"9bf5e563";i:622;s:8:"04fa51af";i:623;s:8:"dd86eeb1";i:624;i:0;i:625;i:2;}}'); +} catch (\Exception $e) { + echo $e->getMessage() . PHP_EOL; +} + +echo \Random\Engine\PcgOneseq128XslRr64::class . PHP_EOL; +// malformed hex +try { + unserialize('O:33:"Random\Engine\PcgOneseq128XslRr64":2:{i:0;a:0:{}i:1;a:2:{i:0;s:15:"c6d571c37c41a8d";i:1;s:16:"345e7e82265d6e27";}}'); +} catch (\Exception $e) { + echo $e->getMessage() . PHP_EOL; +} + +// invalid hex +try { + unserialize('O:33:"Random\Engine\PcgOneseq128XslRr64":2:{i:0;a:0:{}i:1;a:2:{i:0;s:16:"c6d571c37c41a8d1";i:1;s:16:"345e7e82265d6e2g";}}'); +} catch (\Exception $e) { + echo $e->getMessage() . PHP_EOL; +} + +echo \Random\Engine\Xoshiro256StarStar::class . PHP_EOL; +// malformed hex +try { + unserialize('O:32:"Random\Engine\Xoshiro256StarStar":2:{i:0;a:0:{}i:1;a:4:{i:0;s:15:"db1c182f1bf60cb";i:1;s:16:"2465f04d36a1c797";i:2;s:16:"da25c09be4fabe33";i:3;s:16:"33a0d052f241624e";}}'); +} catch (\Exception $e) { + echo $e->getMessage() . PHP_EOL; +} + +// invalid hex +try { + unserialize('O:32:"Random\Engine\Xoshiro256StarStar":2:{i:0;a:0:{}i:1;a:4:{i:0;s:16:"db1c182f1bf60cbb";i:1;s:16:"2465f04d36a1c797";i:2;s:16:"da25c09be4fabe33";i:3;s:16:"33a0d052f241624g";}}'); +} catch (\Exception $e) { + echo $e->getMessage() . PHP_EOL; +} + +?> +--EXPECT-- +Random\Engine\Mt19937 +Engine unserialize failed +Engine unserialize failed +Engine unserialize failed +Engine unserialize failed +Random\Engine\PcgOneseq128XslRr64 +Engine unserialize failed +Engine unserialize failed +Random\Engine\Xoshiro256StarStar +Engine unserialize failed +Engine unserialize failed diff --git a/ext/random/tests/02_engine/all_serialize_native.phpt b/ext/random/tests/02_engine/all_serialize_native.phpt new file mode 100644 index 0000000000000..768979a88bdfa --- /dev/null +++ b/ext/random/tests/02_engine/all_serialize_native.phpt @@ -0,0 +1,27 @@ +--TEST-- +Random: Engine: serialize: native +--FILE-- +generate(); + } + $engine2 = unserialize(serialize($engine)); + for ($i = 0; $i < 5000; $i++) { + if ($engine->generate() !== $engine2->generate()) { + $className = $engine::class; + die("failure class: {$className} i: {$i}"); + } + } +} + +die('success'); +?> +--EXPECT-- +success diff --git a/ext/random/tests/02_engine/all_serialize_user.phpt b/ext/random/tests/02_engine/all_serialize_user.phpt new file mode 100644 index 0000000000000..7d2b05bfdba37 --- /dev/null +++ b/ext/random/tests/02_engine/all_serialize_user.phpt @@ -0,0 +1,48 @@ +--TEST-- +Random: Engine: serialize: user +--FILE-- +count); + } +} + +final class User32 implements \Random\Engine +{ + private int $count = 0; + + public function generate(): string + { + return \pack('V', ++$this->count); + } +} + +$engines = []; +if (\PHP_INT_SIZE >= 8) { + $engines[] = new \User64(); +} +$engines[] = new \User32(); + +foreach ($engines as $engine) { + for ($i = 0; $i < 1000; $i++) { + $engine->generate(); + } + $engine2 = unserialize(serialize($engine)); + for ($i = 0; $i < 5000; $i++) { + if ($engine->generate() !== $engine2->generate()) { + $className = $engine::class; + die("failure class: {$className} i: {$i}"); + } + } +} + +die('success'); +?> +--EXPECT-- +success diff --git a/ext/random/tests/02_engine/mt19937_error.phpt b/ext/random/tests/02_engine/mt19937_error.phpt new file mode 100644 index 0000000000000..fe72bb8898c27 --- /dev/null +++ b/ext/random/tests/02_engine/mt19937_error.phpt @@ -0,0 +1,14 @@ +--TEST-- +Random: Engine: Mt19937: error pattern +--FILE-- +getMessage() . PHP_EOL; +} + +?> +--EXPECT-- +Random\Engine\Mt19937::__construct(): Argument #2 ($mode) mode must be MT_RAND_MT19937 or MT_RAND_PHP diff --git a/ext/random/tests/02_engine/mt19937_serialize.phpt b/ext/random/tests/02_engine/mt19937_serialize.phpt new file mode 100644 index 0000000000000..5621263793f2f --- /dev/null +++ b/ext/random/tests/02_engine/mt19937_serialize.phpt @@ -0,0 +1,10 @@ +--TEST-- +Random: Engine: Mt19937: serialize +--FILE-- + +--EXPECT-- +O:21:"Random\Engine\Mt19937":2:{i:0;a:0:{}i:1;a:626:{i:0;s:8:"5aa6b986";i:1;s:8:"8660cc14";i:2;s:8:"c0b631ca";i:3;s:8:"e85464ad";i:4;s:8:"70fa6108";i:5;s:8:"c5ed9c3c";i:6;s:8:"b05b7ff1";i:7;s:8:"faf33a3a";i:8;s:8:"ab7c0e61";i:9;s:8:"2d4c9c37";i:10;s:8:"daffe918";i:11;s:8:"644f25b9";i:12;s:8:"fdb352e5";i:13;s:8:"434dafa9";i:14;s:8:"a3c9826e";i:15;s:8:"923dfd8c";i:16;s:8:"f641a225";i:17;s:8:"d87134b3";i:18;s:8:"c3b7926e";i:19;s:8:"c880b60d";i:20;s:8:"975f362d";i:21;s:8:"48192b77";i:22;s:8:"6f7dd08b";i:23;s:8:"7d4ad4f8";i:24;s:8:"d805b910";i:25;s:8:"8c98b365";i:26;s:8:"89e54af9";i:27;s:8:"e5257a3c";i:28;s:8:"8f596624";i:29;s:8:"3f42f88a";i:30;s:8:"6a7e95cc";i:31;s:8:"e1349e4d";i:32;s:8:"4539bc92";i:33;s:8:"045a3148";i:34;s:8:"c27cf7b9";i:35;s:8:"c64e8009";i:36;s:8:"dc5ed02e";i:37;s:8:"5753c741";i:38;s:8:"50be0a82";i:39;s:8:"822da0ee";i:40;s:8:"42fdb3c6";i:41;s:8:"c668fc80";i:42;s:8:"262376c6";i:43;s:8:"17b998c6";i:44;s:8:"1f3aac02";i:45;s:8:"6f939c7e";i:46;s:8:"21c099f4";i:47;s:8:"0f4b5c76";i:48;s:8:"64799ac0";i:49;s:8:"45d3bb99";i:50;s:8:"8ff3eb79";i:51;s:8:"61996264";i:52;s:8:"2b9ab1f4";i:53;s:8:"a0d0c50f";i:54;s:8:"f08713ce";i:55;s:8:"6b1cf9d8";i:56;s:8:"52d92cc5";i:57;s:8:"34bcec6f";i:58;s:8:"83ffa063";i:59;s:8:"a002321d";i:60;s:8:"386c46d9";i:61;s:8:"45e2c63e";i:62;s:8:"f481bdf4";i:63;s:8:"df58facb";i:64;s:8:"1781e49b";i:65;s:8:"1d968a6b";i:66;s:8:"8aa7fdd2";i:67;s:8:"631ac8cd";i:68;s:8:"8090ff8e";i:69;s:8:"4ddd4a5d";i:70;s:8:"ff6d8193";i:71;s:8:"39e18244";i:72;s:8:"4efe15db";i:73;s:8:"3fe64cd5";i:74;s:8:"e693a97f";i:75;s:8:"807a34e9";i:76;s:8:"6dc16ae7";i:77;s:8:"a1e1ed1d";i:78;s:8:"605bdc86";i:79;s:8:"abb1830e";i:80;s:8:"2baabeda";i:81;s:8:"fc0f66ad";i:82;s:8:"9cf4fb96";i:83;s:8:"b50ff764";i:84;s:8:"67c37a3d";i:85;s:8:"15253035";i:86;s:8:"387fce47";i:87;s:8:"5c3299a5";i:88;s:8:"125602b9";i:89;s:8:"daeb445c";i:90;s:8:"289e5a8d";i:91;s:8:"b47cd48c";i:92;s:8:"8664db23";i:93;s:8:"b9352af8";i:94;s:8:"51684d88";i:95;s:8:"b204cbc9";i:96;s:8:"e1b6becc";i:97;s:8:"dbbe0ee6";i:98;s:8:"89e54027";i:99;s:8:"a6512564";i:100;s:8:"3aab8d17";i:101;s:8:"7cab5272";i:102;s:8:"1804e981";i:103;s:8:"0bfebe2c";i:104;s:8:"7c082a4c";i:105;s:8:"06bc80c3";i:106;s:8:"c63db839";i:107;s:8:"59edb59d";i:108;s:8:"b52d9655";i:109;s:8:"efce041b";i:110;s:8:"57c1f890";i:111;s:8:"15499697";i:112;s:8:"e0ad3e3a";i:113;s:8:"408d4c16";i:114;s:8:"e53cf468";i:115;s:8:"6ea3d84f";i:116;s:8:"0a2bc4f2";i:117;s:8:"27b59259";i:118;s:8:"d223514b";i:119;s:8:"434c4185";i:120;s:8:"999c71df";i:121;s:8:"46134706";i:122;s:8:"f7f048d0";i:123;s:8:"7f5cfe34";i:124;s:8:"9bdc475c";i:125;s:8:"c1cf42ea";i:126;s:8:"65be324b";i:127;s:8:"68c7a1dc";i:128;s:8:"a7c8c5a2";i:129;s:8:"ecc4cd8a";i:130;s:8:"a761615c";i:131;s:8:"718ec534";i:132;s:8:"c8b086fc";i:133;s:8:"ead759f9";i:134;s:8:"eb9df4a0";i:135;s:8:"ec5b25f9";i:136;s:8:"3a6ff94e";i:137;s:8:"4511a3de";i:138;s:8:"9034f1c6";i:139;s:8:"5c1c6d14";i:140;s:8:"a228ed46";i:141;s:8:"ffe19f2f";i:142;s:8:"f21f68cf";i:143;s:8:"6c2235a4";i:144;s:8:"e0aed51a";i:145;s:8:"2ade527b";i:146;s:8:"65f3c758";i:147;s:8:"f6e868b7";i:148;s:8:"49b770a5";i:149;s:8:"48ab9158";i:150;s:8:"ab07a671";i:151;s:8:"4ef74251";i:152;s:8:"f4cad644";i:153;s:8:"1576c59e";i:154;s:8:"3882bbd1";i:155;s:8:"e49f32c0";i:156;s:8:"c67b757c";i:157;s:8:"3528c5d8";i:158;s:8:"371c3e34";i:159;s:8:"7f1d614f";i:160;s:8:"b9e19e66";i:161;s:8:"80a1d97f";i:162;s:8:"7f93c9f3";i:163;s:8:"4e82ea48";i:164;s:8:"9675e170";i:165;s:8:"aa54caef";i:166;s:8:"3bb9838e";i:167;s:8:"9c0d0a2b";i:168;s:8:"2595d91f";i:169;s:8:"84cc7ff2";i:170;s:8:"9457c4ee";i:171;s:8:"405b6bc8";i:172;s:8:"5aa668a4";i:173;s:8:"e94dfca2";i:174;s:8:"89c0d739";i:175;s:8:"459f8eb3";i:176;s:8:"76b95b42";i:177;s:8:"0e5ceafb";i:178;s:8:"55d4eaee";i:179;s:8:"a55a4784";i:180;s:8:"8c23e133";i:181;s:8:"0994f794";i:182;s:8:"a8d4d1b7";i:183;s:8:"0a50b177";i:184;s:8:"65409f44";i:185;s:8:"acf34e81";i:186;s:8:"e32f278e";i:187;s:8:"7aa21660";i:188;s:8:"9da66881";i:189;s:8:"5c4df7c5";i:190;s:8:"b21f8a4e";i:191;s:8:"d2cc6756";i:192;s:8:"8716f97d";i:193;s:8:"aabd84c5";i:194;s:8:"2e0a965a";i:195;s:8:"b4acc29c";i:196;s:8:"ed3be992";i:197;s:8:"867d9400";i:198;s:8:"a696ba3e";i:199;s:8:"8e2af3d9";i:200;s:8:"8fd95ea9";i:201;s:8:"930903d8";i:202;s:8:"4508dbb1";i:203;s:8:"80598d21";i:204;s:8:"df87fb74";i:205;s:8:"9d019d24";i:206;s:8:"05d5ce2e";i:207;s:8:"ed69bcfe";i:208;s:8:"f83a8d70";i:209;s:8:"750b10bd";i:210;s:8:"c0df892c";i:211;s:8:"df41f215";i:212;s:8:"03df46e3";i:213;s:8:"4e6dce66";i:214;s:8:"ea45a428";i:215;s:8:"cbbf3ff6";i:216;s:8:"f931c7b2";i:217;s:8:"80d19eab";i:218;s:8:"0e2c13da";i:219;s:8:"4b99ee8b";i:220;s:8:"2311d69e";i:221;s:8:"ca9050a7";i:222;s:8:"a4d9eec1";i:223;s:8:"ee665c77";i:224;s:8:"0714f961";i:225;s:8:"81f5be46";i:226;s:8:"420ea4bf";i:227;s:8:"281c28f0";i:228;s:8:"9936ab3f";i:229;s:8:"e4ca8936";i:230;s:8:"224d247b";i:231;s:8:"a564dffe";i:232;s:8:"d15045d2";i:233;s:8:"97d01e8f";i:234;s:8:"71793232";i:235;s:8:"43fe461e";i:236;s:8:"3b50916b";i:237;s:8:"ff242439";i:238;s:8:"a748a0a9";i:239;s:8:"ff51a2a6";i:240;s:8:"dec0ad0d";i:241;s:8:"4bcf2a2e";i:242;s:8:"2be6a97f";i:243;s:8:"eb3e636a";i:244;s:8:"81e31d64";i:245;s:8:"6ca4db9d";i:246;s:8:"bc5bd290";i:247;s:8:"ed2d2ba4";i:248;s:8:"00c46d55";i:249;s:8:"27578407";i:250;s:8:"bedbbcd8";i:251;s:8:"18172abc";i:252;s:8:"eaa5b9cf";i:253;s:8:"9e87ab84";i:254;s:8:"a0e2741c";i:255;s:8:"3bb931ae";i:256;s:8:"3a7b75cd";i:257;s:8:"1052cbf5";i:258;s:8:"b7afd060";i:259;s:8:"1d143d51";i:260;s:8:"17fb7422";i:261;s:8:"523fd346";i:262;s:8:"50429afd";i:263;s:8:"308ece7b";i:264;s:8:"285aa5c1";i:265;s:8:"d8afd736";i:266;s:8:"2e30512e";i:267;s:8:"af78ce36";i:268;s:8:"5967e738";i:269;s:8:"e19795de";i:270;s:8:"6d416e39";i:271;s:8:"23570261";i:272;s:8:"23106924";i:273;s:8:"a4b9add9";i:274;s:8:"baa83e79";i:275;s:8:"97ca48d3";i:276;s:8:"f369a004";i:277;s:8:"fa521bf6";i:278;s:8:"312e9e5e";i:279;s:8:"3acdb850";i:280;s:8:"485cbad7";i:281;s:8:"ddc2d365";i:282;s:8:"d8b87c13";i:283;s:8:"51f6aa7a";i:284;s:8:"b1c231ce";i:285;s:8:"926b9e57";i:286;s:8:"32bd5241";i:287;s:8:"b72cf0a0";i:288;s:8:"0cd99f51";i:289;s:8:"80815c0e";i:290;s:8:"a3459115";i:291;s:8:"5bb05439";i:292;s:8:"ee67a282";i:293;s:8:"65e533c5";i:294;s:8:"62e57b7a";i:295;s:8:"0f4a42fe";i:296;s:8:"ff5b8045";i:297;s:8:"cb9d60cf";i:298;s:8:"ffc891cd";i:299;s:8:"514d6027";i:300;s:8:"5b92df3b";i:301;s:8:"1fb47b1a";i:302;s:8:"a2382db8";i:303;s:8:"6f8b23cd";i:304;s:8:"db72737a";i:305;s:8:"d2d5022b";i:306;s:8:"eef762a7";i:307;s:8:"771fc2ed";i:308;s:8:"6581afb1";i:309;s:8:"64d7ffbd";i:310;s:8:"93a15a65";i:311;s:8:"fdc8b81b";i:312;s:8:"6369357b";i:313;s:8:"a8a58d2e";i:314;s:8:"d9ab2ed6";i:315;s:8:"0465340d";i:316;s:8:"eed4e3f1";i:317;s:8:"7e15ef9d";i:318;s:8:"99444e57";i:319;s:8:"959a6881";i:320;s:8:"e7498e10";i:321;s:8:"d7d0699e";i:322;s:8:"12372566";i:323;s:8:"b9f97c89";i:324;s:8:"5e688172";i:325;s:8:"8105e3b8";i:326;s:8:"627e9227";i:327;s:8:"67edb47d";i:328;s:8:"74e69d5f";i:329;s:8:"b8c5df2c";i:330;s:8:"14f06e80";i:331;s:8:"1a0af27f";i:332;s:8:"fa6ad68b";i:333;s:8:"d5938b0d";i:334;s:8:"2df9b8cc";i:335;s:8:"d90bd16c";i:336;s:8:"18779d24";i:337;s:8:"e1b81866";i:338;s:8:"dc51e106";i:339;s:8:"5078c0ce";i:340;s:8:"8fde3be3";i:341;s:8:"85939b08";i:342;s:8:"50f5565d";i:343;s:8:"281ec5f0";i:344;s:8:"ee2c4dfb";i:345;s:8:"89df2863";i:346;s:8:"0444ef85";i:347;s:8:"d52671b6";i:348;s:8:"84695c5d";i:349;s:8:"ceb19fc9";i:350;s:8:"8cd19595";i:351;s:8:"9773504b";i:352;s:8:"72031351";i:353;s:8:"285de56c";i:354;s:8:"08d949a1";i:355;s:8:"d1d24d60";i:356;s:8:"41c57eff";i:357;s:8:"ded22dfc";i:358;s:8:"9f799eb2";i:359;s:8:"ac14c164";i:360;s:8:"23ab3dac";i:361;s:8:"76de10e0";i:362;s:8:"72c2ac0b";i:363;s:8:"cce93127";i:364;s:8:"32bbb7e5";i:365;s:8:"b8af671a";i:366;s:8:"24dfc65a";i:367;s:8:"d7c8431f";i:368;s:8:"5cf3dffd";i:369;s:8:"7a93645c";i:370;s:8:"36bb66f8";i:371;s:8:"dc055709";i:372;s:8:"93c58efc";i:373;s:8:"11b9a1e6";i:374;s:8:"e487f36b";i:375;s:8:"7c3bcb46";i:376;s:8:"e4d724a3";i:377;s:8:"b732d653";i:378;s:8:"afc9c219";i:379;s:8:"ea658830";i:380;s:8:"b9052af6";i:381;s:8:"aa46a473";i:382;s:8:"1b37a35e";i:383;s:8:"0ba14ee9";i:384;s:8:"c7e150ac";i:385;s:8:"055ffa96";i:386;s:8:"e2bf88fb";i:387;s:8:"88f1f936";i:388;s:8:"488c7bdb";i:389;s:8:"a9dacbd1";i:390;s:8:"aaeea26f";i:391;s:8:"237af1cb";i:392;s:8:"e3dbdb50";i:393;s:8:"c8e5949e";i:394;s:8:"e70a989a";i:395;s:8:"fa540d6d";i:396;s:8:"f055b2f2";i:397;s:8:"1fc8acd4";i:398;s:8:"accb1a9b";i:399;s:8:"bedfb89c";i:400;s:8:"3d243ec0";i:401;s:8:"d2f3f2d8";i:402;s:8:"d83e7c53";i:403;s:8:"4f12e12d";i:404;s:8:"f48f33d1";i:405;s:8:"9555d943";i:406;s:8:"db853a55";i:407;s:8:"506a95b4";i:408;s:8:"408155d1";i:409;s:8:"32a384a0";i:410;s:8:"21c34eb4";i:411;s:8:"93e2e1ae";i:412;s:8:"88582a10";i:413;s:8:"1f065648";i:414;s:8:"96c3c81b";i:415;s:8:"412bc358";i:416;s:8:"11a4e990";i:417;s:8:"805ead73";i:418;s:8:"ec8ca56e";i:419;s:8:"a2ed4bdc";i:420;s:8:"ac408a10";i:421;s:8:"4ca98f96";i:422;s:8:"0686b6df";i:423;s:8:"9e9f7ae4";i:424;s:8:"07d5f658";i:425;s:8:"1c121a0c";i:426;s:8:"c11c8e82";i:427;s:8:"13f2c6d7";i:428;s:8:"e9f6ad9c";i:429;s:8:"bc1dc285";i:430;s:8:"e271fe4b";i:431;s:8:"34114ee5";i:432;s:8:"92ad1c39";i:433;s:8:"5ab5504b";i:434;s:8:"4f7118a7";i:435;s:8:"d1ceec2f";i:436;s:8:"adfd7622";i:437;s:8:"f4f4a9c0";i:438;s:8:"2d4f21e3";i:439;s:8:"3b4bed9e";i:440;s:8:"42ac5810";i:441;s:8:"7eae8db5";i:442;s:8:"09dc329e";i:443;s:8:"3c723314";i:444;s:8:"7fb50c08";i:445;s:8:"71fd69dd";i:446;s:8:"013ce542";i:447;s:8:"0add0d73";i:448;s:8:"465d495a";i:449;s:8:"9f8ddb9d";i:450;s:8:"f293e79e";i:451;s:8:"d6f59d72";i:452;s:8:"ac22e38f";i:453;s:8:"e96d5751";i:454;s:8:"fba79717";i:455;s:8:"39fedf2f";i:456;s:8:"3fb25196";i:457;s:8:"fcdaa825";i:458;s:8:"9a960022";i:459;s:8:"5371af3d";i:460;s:8:"df7faf0a";i:461;s:8:"82c22c85";i:462;s:8:"dfbbae9f";i:463;s:8:"403a4b84";i:464;s:8:"bc938282";i:465;s:8:"d2355fbc";i:466;s:8:"8d72b179";i:467;s:8:"dced02b2";i:468;s:8:"227b82e3";i:469;s:8:"24c60db6";i:470;s:8:"45092b73";i:471;s:8:"767c0e1d";i:472;s:8:"7eb5c592";i:473;s:8:"d0b356d9";i:474;s:8:"dc95ee45";i:475;s:8:"39aa5820";i:476;s:8:"9e6e1868";i:477;s:8:"ffe72d78";i:478;s:8:"82ae0503";i:479;s:8:"2ea981ad";i:480;s:8:"6935faba";i:481;s:8:"69c2ba98";i:482;s:8:"69dd219f";i:483;s:8:"860e0d0d";i:484;s:8:"5f451aa3";i:485;s:8:"a9d5f07d";i:486;s:8:"ec623682";i:487;s:8:"04d8adba";i:488;s:8:"717e8c76";i:489;s:8:"f9c1fb75";i:490;s:8:"ab4c9e06";i:491;s:8:"3895ef4d";i:492;s:8:"37c09060";i:493;s:8:"f3b7c652";i:494;s:8:"74dc421e";i:495;s:8:"97d458be";i:496;s:8:"1a1ddea1";i:497;s:8:"001c68ad";i:498;s:8:"680f033f";i:499;s:8:"b2e9bd38";i:500;s:8:"54192958";i:501;s:8:"b6dd0693";i:502;s:8:"9d149906";i:503;s:8:"9ba5479a";i:504;s:8:"18ba25e8";i:505;s:8:"16fa3e51";i:506;s:8:"1e74b698";i:507;s:8:"c5932028";i:508;s:8:"5a6cf6cd";i:509;s:8:"6fa90e13";i:510;s:8:"bcabdecb";i:511;s:8:"ae23af9a";i:512;s:8:"76b297eb";i:513;s:8:"d1332632";i:514;s:8:"693a55c3";i:515;s:8:"7c4beb9b";i:516;s:8:"409a0a15";i:517;s:8:"60f68335";i:518;s:8:"febcb934";i:519;s:8:"f32c6f8c";i:520;s:8:"aacacf0d";i:521;s:8:"d1f56e99";i:522;s:8:"d65bcf00";i:523;s:8:"361c9633";i:524;s:8:"cd34d7f4";i:525;s:8:"37d38e62";i:526;s:8:"942dee9d";i:527;s:8:"f41c9445";i:528;s:8:"7e9a8b5d";i:529;s:8:"4b941ed7";i:530;s:8:"c6256dce";i:531;s:8:"6c285146";i:532;s:8:"5920147e";i:533;s:8:"934d59ed";i:534;s:8:"5f400a1d";i:535;s:8:"1a5de58c";i:536;s:8:"11e601de";i:537;s:8:"cf4da287";i:538;s:8:"756a54e4";i:539;s:8:"9f2b1015";i:540;s:8:"3a3df642";i:541;s:8:"5d024d64";i:542;s:8:"2d617393";i:543;s:8:"e1b4ec53";i:544;s:8:"24996b5e";i:545;s:8:"7a271bf1";i:546;s:8:"68d7da60";i:547;s:8:"06d3bc73";i:548;s:8:"f21095f7";i:549;s:8:"93e6bfd9";i:550;s:8:"e80b015b";i:551;s:8:"4ddd4ede";i:552;s:8:"cec3d813";i:553;s:8:"281f2a65";i:554;s:8:"ffa068fb";i:555;s:8:"60e712e7";i:556;s:8:"4abcecac";i:557;s:8:"eef01060";i:558;s:8:"8e79e897";i:559;s:8:"b0cd39fe";i:560;s:8:"28cc9604";i:561;s:8:"c7bd58e0";i:562;s:8:"c73b1765";i:563;s:8:"e87d5e07";i:564;s:8:"03b7710d";i:565;s:8:"5f64c5dc";i:566;s:8:"4ae7b6f1";i:567;s:8:"401b9876";i:568;s:8:"9a162522";i:569;s:8:"2a1d6390";i:570;s:8:"c115eeff";i:571;s:8:"e549f7df";i:572;s:8:"a159c579";i:573;s:8:"48a44ff6";i:574;s:8:"e65fd1fc";i:575;s:8:"bb74cd7b";i:576;s:8:"87cc0383";i:577;s:8:"052755ee";i:578;s:8:"eba29b21";i:579;s:8:"88e2efa9";i:580;s:8:"a1a521b9";i:581;s:8:"ffb6f27b";i:582;s:8:"4fea3248";i:583;s:8:"0f46ad03";i:584;s:8:"58902f76";i:585;s:8:"cfb3f87e";i:586;s:8:"efb3d85c";i:587;s:8:"1e0f4648";i:588;s:8:"f95eda47";i:589;s:8:"c5220b0b";i:590;s:8:"e6574ef8";i:591;s:8:"f34b162e";i:592;s:8:"09b08b14";i:593;s:8:"dae53353";i:594;s:8:"ccc600dd";i:595;s:8:"b3ae8f4f";i:596;s:8:"daed379c";i:597;s:8:"113f5973";i:598;s:8:"6a66bb0a";i:599;s:8:"697817f3";i:600;s:8:"00b78167";i:601;s:8:"1e7c2ed9";i:602;s:8:"65f8a3c5";i:603;s:8:"c50f5c14";i:604;s:8:"a1665ca7";i:605;s:8:"d31c5017";i:606;s:8:"2e421193";i:607;s:8:"cb8d29a9";i:608;s:8:"fa9723b5";i:609;s:8:"8a4d18e4";i:610;s:8:"88081a19";i:611;s:8:"242623e4";i:612;s:8:"86202155";i:613;s:8:"ab55982f";i:614;s:8:"f1a3c261";i:615;s:8:"c44eaa91";i:616;s:8:"06c4716f";i:617;s:8:"60a25216";i:618;s:8:"dcce86a3";i:619;s:8:"bfcec0cd";i:620;s:8:"fb4170ed";i:621;s:8:"9bf5e563";i:622;s:8:"04fa51af";i:623;s:8:"dd86eeb1";i:624;i:0;i:625;i:0;}} diff --git a/ext/random/tests/02_engine/mt19937_value.phpt b/ext/random/tests/02_engine/mt19937_value.phpt new file mode 100644 index 0000000000000..e90d3e69e3e05 --- /dev/null +++ b/ext/random/tests/02_engine/mt19937_value.phpt @@ -0,0 +1,15 @@ +--TEST-- +Random: Engine: Mt19937: value +--FILE-- +generate(); +} + +echo \bin2hex($engine->generate()); +?> +--EXPECT-- +60fe95d9 diff --git a/ext/random/tests/02_engine/pcgoneseq128xslrr64_seed.phpt b/ext/random/tests/02_engine/pcgoneseq128xslrr64_seed.phpt new file mode 100644 index 0000000000000..8b8c50d84c1c1 --- /dev/null +++ b/ext/random/tests/02_engine/pcgoneseq128xslrr64_seed.phpt @@ -0,0 +1,43 @@ +--TEST-- +Random: Engine: PcgOneseq128XslRr64: seed +--FILE-- +getMessage() . PHP_EOL; +} + +try { + $engine = new Random\Engine\PcgOneseq128XslRr64('foobar'); +} catch (\Throwable $e) { + echo $e->getMessage() . PHP_EOL; +} + +$engine = new \Random\Engine\PcgOneseq128XslRr64("\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08"); + +\var_dump($engine); + +for ($i = 0; $i < 1000; $i++) { + $engine->generate(); +} +\var_dump(\bin2hex($engine->generate())); + +?> +--EXPECTF-- +Random\Engine\PcgOneseq128XslRr64::__construct(): Argument #1 ($seed) must be of type string|int|null, float given +Random\Engine\PcgOneseq128XslRr64::__construct(): Argument #1 ($seed) state strings must be 16 bytes +object(Random\Engine\PcgOneseq128XslRr64)#%d (%d) { + ["__states"]=> + array(2) { + [0]=> + string(16) "7afbdfd5830d8250" + [1]=> + string(16) "dfc50b6959b3bafc" + } +} +string(16) "c42016cd9005ef2e" diff --git a/ext/random/tests/02_engine/pcgoneseq128xslrr64_serialize.phpt b/ext/random/tests/02_engine/pcgoneseq128xslrr64_serialize.phpt new file mode 100644 index 0000000000000..d0572138fcf6e --- /dev/null +++ b/ext/random/tests/02_engine/pcgoneseq128xslrr64_serialize.phpt @@ -0,0 +1,10 @@ +--TEST-- +Random: Engine: PcgOneseq128XslRr64: serialize +--FILE-- + +--EXPECT-- +O:33:"Random\Engine\PcgOneseq128XslRr64":2:{i:0;a:0:{}i:1;a:2:{i:0;s:16:"c6d571c37c41a8d1";i:1;s:16:"345e7e82265d6e27";}} diff --git a/ext/random/tests/02_engine/pcgoneseq128xslrr64_value.phpt b/ext/random/tests/02_engine/pcgoneseq128xslrr64_value.phpt new file mode 100644 index 0000000000000..c363c9e4d1cd3 --- /dev/null +++ b/ext/random/tests/02_engine/pcgoneseq128xslrr64_value.phpt @@ -0,0 +1,17 @@ +--TEST-- +Random: Engine: PcgOneseq128XslRr64: value +--FILE-- +generate(); +} + +$engine->jump(1234567); + +echo \bin2hex($engine->generate()); +?> +--EXPECT-- +b88e2a0f23720a1d diff --git a/ext/random/tests/02_engine/user_compatibility.phpt b/ext/random/tests/02_engine/user_compatibility.phpt new file mode 100644 index 0000000000000..824dc367da449 --- /dev/null +++ b/ext/random/tests/02_engine/user_compatibility.phpt @@ -0,0 +1,63 @@ +--TEST-- +Random: Engine: User: compatibility +--FILE-- +engine->generate(); + } +}; + +for ($i = 0; $i < 1000; $i++) { + if ($native_engine->generate() !== $user_engine->generate()) { + die('failure Mt19937'); + } +} + +$native_engine = new \Random\Engine\PcgOneseq128XslRr64(1234); +$user_engine = new class () implements \Random\Engine { + public function __construct(private $engine = new \Random\Engine\PcgOneseq128XslRr64(1234)) + { + } + + public function generate(): string + { + return $this->engine->generate(); + } +}; + +for ($i = 0; $i < 1000; $i++) { + if ($native_engine->generate() !== $user_engine->generate()) { + die('failure PcgOneseq128XslRr64'); + } +} + +$native_engine = new \Random\Engine\Xoshiro256StarStar(1234); +$user_engine = new class () implements \Random\Engine { + public function __construct(private $engine = new \Random\Engine\Xoshiro256StarStar(1234)) + { + } + + public function generate(): string + { + return $this->engine->generate(); + } +}; + +for ($i = 0; $i < 1000; $i++) { + if ($native_engine->generate() !== $user_engine->generate()) { + die('failure Xoshiro256StarStar'); + } +} + +die('success'); +?> +--EXPECT-- +success diff --git a/ext/random/tests/02_engine/user_xoshiro128plusplus.phpt b/ext/random/tests/02_engine/user_xoshiro128plusplus.phpt new file mode 100644 index 0000000000000..9759e01a12994 --- /dev/null +++ b/ext/random/tests/02_engine/user_xoshiro128plusplus.phpt @@ -0,0 +1,61 @@ +--TEST-- +Random: Engine: User: Xoshiro128++ +--SKIPIF-- + +--FILE-- + + */ + +final class Xoshiro128PP implements \Random\Engine +{ + private function __construct( + private int $s0, + private int $s1, + private int $s2, + private int $s3 + ) { + } + + public static function fromNumbers($s0, $s1, $s2, $s3) + { + return new self($s0, $s1, $s2, $s3); + } + + private static function rotl($x, $k) + { + return (($x << $k) | ($x >> (32 - $k))) & 0xFFFFFFFF; + } + + public function generate(): string + { + $result = (self::rotl(($this->s0 + $this->s3) & 0xFFFFFFFF, 7) + $this->s0) & 0xFFFFFFFF; + + $t = ($this->s1 << 9) & 0xFFFFFFFF; + + $this->s2 ^= $this->s0; + $this->s3 ^= $this->s1; + $this->s1 ^= $this->s2; + $this->s0 ^= $this->s3; + + $this->s2 ^= $t; + + $this->s3 = self::rotl($this->s3, 11); + + return \pack('V', $result); + } +} + +$g = Xoshiro128PP::fromNumbers(1, 2, 3, 4); + +for ($i = 0; $i < 102400; $i++) { + $g->generate(); +} + +die(\bin2hex($g->generate())); + +?> +--EXPECT-- +fa3c872c diff --git a/ext/random/tests/02_engine/xoshiro256starstar_seed.phpt b/ext/random/tests/02_engine/xoshiro256starstar_seed.phpt new file mode 100644 index 0000000000000..b495ea008eee3 --- /dev/null +++ b/ext/random/tests/02_engine/xoshiro256starstar_seed.phpt @@ -0,0 +1,47 @@ +--TEST-- +Random: Engine: Xoshiro256StarStar: seed +--FILE-- +getMessage() . PHP_EOL; +} + +try { + $engine = new Random\Engine\Xoshiro256StarStar('foobar'); +} catch (\Throwable $e) { + echo $e->getMessage() . PHP_EOL; +} + +$engine = new \Random\Engine\Xoshiro256StarStar("\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08"); + +\var_dump($engine); + +for ($i = 0; $i < 1000; $i++) { + $engine->generate(); +} +\var_dump(\bin2hex($engine->generate())); + +?> +--EXPECTF-- +Random\Engine\Xoshiro256StarStar::__construct(): Argument #1 ($seed) must be of type string|int|null, float given +Random\Engine\Xoshiro256StarStar::__construct(): Argument #1 ($seed) state strings must be 32 bytes +object(Random\Engine\Xoshiro256StarStar)#%d (%d) { + ["__states"]=> + array(4) { + [0]=> + string(16) "0102030405060708" + [1]=> + string(16) "0102030405060708" + [2]=> + string(16) "0102030405060708" + [3]=> + string(16) "0102030405060708" + } +} +string(16) "90a025df9300cfd1" diff --git a/ext/random/tests/02_engine/xoshiro256starstar_serialize.phpt b/ext/random/tests/02_engine/xoshiro256starstar_serialize.phpt new file mode 100644 index 0000000000000..7230c3559614a --- /dev/null +++ b/ext/random/tests/02_engine/xoshiro256starstar_serialize.phpt @@ -0,0 +1,10 @@ +--TEST-- +Random: Engine: Xoshiro256StarStar: serialize +--FILE-- + +--EXPECT-- +O:32:"Random\Engine\Xoshiro256StarStar":2:{i:0;a:0:{}i:1;a:4:{i:0;s:16:"db1c182f1bf60cbb";i:1;s:16:"2465f04d36a1c797";i:2;s:16:"da25c09be4fabe33";i:3;s:16:"33a0d052f241624e";}} diff --git a/ext/random/tests/02_engine/xoshiro256starstar_value.phpt b/ext/random/tests/02_engine/xoshiro256starstar_value.phpt new file mode 100644 index 0000000000000..8ec283aab1509 --- /dev/null +++ b/ext/random/tests/02_engine/xoshiro256starstar_value.phpt @@ -0,0 +1,18 @@ +--TEST-- +Random: Engine: Xoshiro256StarStar: value +--FILE-- +generate(); +} + +$engine->jump(); +$engine->jumpLong(); + +echo \bin2hex($engine->generate()); +?> +--EXPECT-- +1f197e9ca7969123 diff --git a/ext/random/tests/03_randomizer/basic.phpt b/ext/random/tests/03_randomizer/basic.phpt new file mode 100644 index 0000000000000..4e4034a1fb916 --- /dev/null +++ b/ext/random/tests/03_randomizer/basic.phpt @@ -0,0 +1,70 @@ +--TEST-- +Random: Randomizer: basic +--FILE-- +getInt(-50, 50); + if ($result > 50 || $result < -50) { + die($engine::class . ': getInt: failure'); + } + } + + // getBytes + for ($i = 0; $i < 1000; $i++) { + $length = \random_int(1, 1024); + if (\strlen($randomizer->getBytes($length)) !== $length) { + die($engine::class . ': getBytes: failure.'); + } + } + + // shuffleArray + $array = range(1, 1000); + $shuffled_array = $randomizer->shuffleArray($array); + (function () use ($array, $shuffled_array): void { + for ($i = 0; $i < count($array); $i++) { + if ($array[$i] !== $shuffled_array[$i]) { + return; + } + } + + die($engine::class . ': shuffleArray: failure.'); + })(); + + // shuffleBytes + $string = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'; + $shuffled_string = $randomizer->shuffleBytes($string); + if ($string === $shuffled_string) { + die($engine::class . ': shuffleBytes: failure.'); + } +} + +die('success'); +?> +--EXPECTF-- +success diff --git a/ext/random/tests/03_randomizer/compatibility_mt.phpt b/ext/random/tests/03_randomizer/compatibility_mt.phpt new file mode 100644 index 0000000000000..89c67d556e0a6 --- /dev/null +++ b/ext/random/tests/03_randomizer/compatibility_mt.phpt @@ -0,0 +1,24 @@ +--TEST-- +Random: Randomizer: Compatibility: Mt19937 +--FILE-- +getInt() !== \mt_rand()) { + die('failure'); + } +} + +$randomizer = new \Random\Randomizer(new \Random\Engine\Mt19937(1234, \MT_RAND_MT19937)); +\mt_srand(1234, \MT_RAND_MT19937); +for ($i = 0; $i < 1000; $i++) { + if ($randomizer->getInt() !== \mt_rand()) { + die('failure'); + } +} + +die('success'); +--EXPECT-- +success diff --git a/ext/random/tests/03_randomizer/compatibility_user.phpt b/ext/random/tests/03_randomizer/compatibility_user.phpt new file mode 100644 index 0000000000000..2a903a52a0462 --- /dev/null +++ b/ext/random/tests/03_randomizer/compatibility_user.phpt @@ -0,0 +1,86 @@ +--TEST-- +Random: Randomizer: Compatibility: user +--FILE-- +engine->generate(); + } +}); +for ($i = 0; $i < 1000; $i++) { + $native = $native_randomizer->getInt(); + $user = $user_randomizer->getInt(); + if ($native !== $user) { + die("failure Mt19937 i: {$i} native: {$native} user: {$user}"); + } +} + +try { + $native_randomizer = new \Random\Randomizer(new \Random\Engine\PcgOneseq128XslRr64(1234)); + $user_randomizer = new \Random\Randomizer(new class () implements \Random\Engine { + public function __construct(private $engine = new \Random\Engine\PcgOneseq128XslRr64(1234)) + { + } + + public function generate(): string + { + return $this->engine->generate(); + } + }); + + for ($i = 0; $i < 1000; $i++) { + $native = $native_randomizer->getInt(); + $user = $user_randomizer->getInt(); + if ($native !== $user) { + die("failure PcgOneseq128XslRr64 i: {$i} native: {$native} user: {$user}"); + } + } +} catch (\RuntimeException $e) { + if (\PHP_INT_SIZE >= 8) { + throw $e; + } + if ($e->getMessage() !== 'Generated value exceeds size of int') { + throw $e; + } +} + +try { + $native_randomizer = new \Random\Randomizer(new \Random\Engine\Xoshiro256StarStar(1234)); + $user_randomizer = new \Random\Randomizer(new class () implements \Random\Engine { + public function __construct(private $engine = new \Random\Engine\Xoshiro256StarStar(1234)) + { + } + + public function generate(): string + { + return $this->engine->generate(); + } + }); + + for ($i = 0; $i < 1000; $i++) { + $native = $native_randomizer->getInt(); + $user = $user_randomizer->getInt(); + if ($native !== $user) { + die("failure Xoshiro256StarStar i: {$i} native: {$native} user: {$user}"); + } + } +} catch (\RuntimeException $e) { + if (\PHP_INT_SIZE >= 8) { + throw $e; + } + if ($e->getMessage() !== 'Generated value exceeds size of int') { + throw $e; + } +} + +die('success'); +?> +--EXPECT-- +success diff --git a/ext/random/tests/03_randomizer/get_bytes.phpt b/ext/random/tests/03_randomizer/get_bytes.phpt new file mode 100644 index 0000000000000..c6b4a59475e72 --- /dev/null +++ b/ext/random/tests/03_randomizer/get_bytes.phpt @@ -0,0 +1,40 @@ +--TEST-- +Random: Randomizer: getBytes +--FILE-- +count > 5) { + die('overflow'); + } + + return match ($this->count++) { + 0 => 'H', + 1 => 'e', + 2 => 'll', + 3 => 'o', + 4 => 'abcdefghijklmnopqrstuvwxyz', // 208 bits + 5 => 'success', + default => \random_bytes(16), + }; + } + } +); + +echo $randomizer->getBytes(5) . PHP_EOL; // Hello + +echo $randomizer->getBytes(8) . PHP_EOL; // abcdefgh (64 bits) + +die($randomizer->getBytes(7)); + +?> +--EXPECTF-- +Hello +abcdefgh +success diff --git a/ext/random/tests/03_randomizer/pick_array_keys.phpt b/ext/random/tests/03_randomizer/pick_array_keys.phpt new file mode 100644 index 0000000000000..168d01dbc42dd --- /dev/null +++ b/ext/random/tests/03_randomizer/pick_array_keys.phpt @@ -0,0 +1,48 @@ +--TEST-- +Random: Randomizer: pickArrayKeys +--FILE-- + 0, 'bar' => 1, 'baz' => 2]; +$list = range(1, 10); + +mt_srand(1234); + +$mapPickOneFunc = array_rand($map, 1); +$mapPickTwoFunc = array_rand($map, 2); + +$listPickOneFunc = array_rand($list, 1); +$listPickTwoFunc = array_rand($list, 2); + +$randomizer = new \Random\Randomizer(new \Random\Engine\Mt19937(1234)); + +[$mapPickOneMethod] = $randomizer->pickArrayKeys($map, 1); +$mapPickTwoMethod = $randomizer->pickArrayKeys($map, 2); + +[$listPickOneMethod] = $randomizer->pickArrayKeys($list, 1); +$listPickTwoMethod = $randomizer->pickArrayKeys($list, 2); + +if ($mapPickOneFunc !== $mapPickOneMethod) { + var_dump($mapPickOneFunc, $mapPickOneMethod); + die('failure mapPickOne'); +} + +if ($mapPickTwoFunc !== $mapPickTwoMethod) { + var_dump($mapPickTwoFunc, $mapPickTwoMethod); + die('failure mapPickTwo'); +} + +if ($listPickOneFunc !== $listPickOneMethod) { + var_dump($listPickOneFunc, $listPickOneMethod); + die('failure listPickOne'); +} + +if ($listPickTwoFunc !== $listPickTwoMethod) { + var_dump($listPickTwoFunc, $listPickOneMethod); + die('failure listPickTwo'); +} + +die('success'); +?> +--EXPECTF-- +success diff --git a/ext/random/tests/03_randomizer/pick_array_keys_error.phpt b/ext/random/tests/03_randomizer/pick_array_keys_error.phpt new file mode 100644 index 0000000000000..98abc82a55018 --- /dev/null +++ b/ext/random/tests/03_randomizer/pick_array_keys_error.phpt @@ -0,0 +1,52 @@ +--TEST-- +Random: Randomizer: pickArrayKeys error pattern +--FILE-- +pickArrayKeys([], 0)); +} catch (\ValueError $e) { + echo $e->getMessage() . "\n"; +} + +try { + var_dump((new \Random\Randomizer())->pickArrayKeys(range(1, 3), 0)); +} catch (\ValueError $e) { + echo $e->getMessage() . "\n"; +} + +try { + var_dump((new \Random\Randomizer())->pickArrayKeys(range(1, 3), -1)); +} catch (\ValueError $e) { + echo $e->getMessage() . "\n"; +} + +try { + var_dump((new \Random\Randomizer())->pickArrayKeys(range(1, 3), 10)); +} catch (\ValueError $e) { + echo $e->getMessage() . "\n"; +} + +var_dump((new \Random\Randomizer())->pickArrayKeys(range(1, 3), 3)); +var_dump((new \Random\Randomizer())->pickArrayKeys(range(1, 3), 2)); + +?> +--EXPECTF-- +Random\Randomizer::pickArrayKeys(): Argument #1 ($array) cannot be empty +Random\Randomizer::pickArrayKeys(): Argument #2 ($num) must be between 1 and the number of elements in argument #1 ($array) +Random\Randomizer::pickArrayKeys(): Argument #2 ($num) must be between 1 and the number of elements in argument #1 ($array) +Random\Randomizer::pickArrayKeys(): Argument #2 ($num) must be between 1 and the number of elements in argument #1 ($array) +array(3) { + [0]=> + int(%d) + [1]=> + int(%d) + [2]=> + int(%d) +} +array(2) { + [0]=> + int(%d) + [1]=> + int(%d) +} diff --git a/ext/random/tests/03_randomizer/readonly.phpt b/ext/random/tests/03_randomizer/readonly.phpt new file mode 100644 index 0000000000000..f1f4a461714e4 --- /dev/null +++ b/ext/random/tests/03_randomizer/readonly.phpt @@ -0,0 +1,43 @@ +--TEST-- +Random: Randomizer: readonly engine +--FILE-- +engine; +if ($one->engine->generate() !== $one_ng_clone->generate()) { + die('invalid result'); +} + +try { + $one->engine = $one_ng_clone; +} catch (\Throwable $e) { + echo $e->getMessage() . PHP_EOL; +} + +$two = new \Random\Randomizer( + new \Random\Engine\Secure() +); + +try { + $two_ng_clone = clone $two->engine; +} catch (\Throwable $e) { + echo $e->getMessage() . PHP_EOL; +} + +try { + $two->engine = $one_ng_clone; +} catch (\Throwable $e) { + echo $e->getMessage() . PHP_EOL; +} + +die('success') +?> +--EXPECT-- +Cannot modify readonly property Random\Randomizer::$engine +Trying to clone an uncloneable object of class Random\Engine\Secure +Cannot modify readonly property Random\Randomizer::$engine +success diff --git a/ext/random/tests/03_randomizer/serialize.phpt b/ext/random/tests/03_randomizer/serialize.phpt new file mode 100644 index 0000000000000..37841a0db106f --- /dev/null +++ b/ext/random/tests/03_randomizer/serialize.phpt @@ -0,0 +1,57 @@ +--TEST-- +Random: Randomizer: serialize +--FILE-- +getInt(\PHP_INT_MIN, \PHP_INT_MAX); + try { + $randomizer2 = unserialize(serialize($randomizer)); + } catch (\Exception $e) { + echo $e->getMessage() . PHP_EOL; + continue; + } + + if ($randomizer->getInt(\PHP_INT_MIN, \PHP_INT_MAX) !== $randomizer2->getInt(\PHP_INT_MIN, \PHP_INT_MAX)) { + die($engine::class . ': failure.'); + } + + echo $engine::class . ': success' . PHP_EOL; +} + +die('success'); +?> +--EXPECTF-- +Random\Engine\Mt19937: success +Random\Engine\Mt19937: success +Random\Engine\PcgOneseq128XslRr64: success +Random\Engine\Xoshiro256StarStar: success +Serialization of 'Random\Engine\Secure' is not allowed +Serialization of 'Random\Engine@anonymous' is not allowed +UserEngine: success +success diff --git a/ext/random/tests/03_randomizer/user_unsafe.phpt b/ext/random/tests/03_randomizer/user_unsafe.phpt new file mode 100644 index 0000000000000..659159671f29a --- /dev/null +++ b/ext/random/tests/03_randomizer/user_unsafe.phpt @@ -0,0 +1,83 @@ +--TEST-- +Random: Randomizer: User: Engine unsafe +--FILE-- +getInt(\PHP_INT_MIN, \PHP_INT_MAX)); +} catch (\RuntimeException $e) { + echo $e->getMessage() . PHP_EOL; +} + +try { + var_dump(bin2hex($randomizer->getBytes(1))); +} catch (\RuntimeException $e) { + echo $e->getMessage() . PHP_EOL; +} + +try { + var_dump($randomizer->shuffleArray(\range(1, 10))); +} catch (\RuntimeException $e) { + echo $e->getMessage() . PHP_EOL; +} + +try { + var_dump($randomizer->shuffleBytes('foobar')); +} catch (\RuntimeException $e) { + echo $e->getMessage() . PHP_EOL; +} + +// Infinite loop +$randomizer = (new \Random\Randomizer( + new class () implements \Random\Engine { + public function generate(): string + { + return "\xff\xff\xff\xff\xff\xff\xff\xff"; + } + } +)); + +try { + var_dump($randomizer->getInt(\PHP_INT_MIN, \PHP_INT_MAX)); +} catch (\RuntimeException $e) { + echo $e->getMessage() . PHP_EOL; +} + +try { + var_dump(bin2hex($randomizer->getBytes(1))); +} catch (\RuntimeException $e) { + echo $e->getMessage() . PHP_EOL; +} + +try { + var_dump($randomizer->shuffleArray(\range(1, 10))); +} catch (\RuntimeException $e) { + echo $e->getMessage() . PHP_EOL; +} + +try { + var_dump($randomizer->shuffleBytes('foobar')); +} catch (\RuntimeException $e) { + echo $e->getMessage() . PHP_EOL; +} + +?> +--EXPECTF-- +Random number generation failed +Random number generation failed +Random number generation failed +Random number generation failed +int(%d) +string(2) "ff" +Random number generation failed +Random number generation failed diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index cf3b024c22b85..8a954a1a089f2 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -27,7 +27,7 @@ #include "php_reflection.h" #include "ext/standard/info.h" #include "ext/standard/sha1.h" -#include "ext/standard/php_random.h" +#include "ext/random/php_random.h" #include "zend.h" #include "zend_API.h" diff --git a/ext/session/session.c b/ext/session/session.c index 75668d53e546c..e691742c4626a 100644 --- a/ext/session/session.c +++ b/ext/session/session.c @@ -37,16 +37,15 @@ #include "php_variables.h" #include "php_session.h" #include "session_arginfo.h" -#include "ext/standard/php_random.h" #include "ext/standard/php_var.h" #include "ext/date/php_date.h" -#include "ext/standard/php_lcg.h" #include "ext/standard/url_scanner_ex.h" #include "ext/standard/info.h" #include "zend_smart_str.h" #include "ext/standard/url.h" #include "ext/standard/basic_functions.h" #include "ext/standard/head.h" +#include "ext/random/php_random.h" #include "mod_files.h" #include "mod_user.h" diff --git a/ext/soap/php_http.c b/ext/soap/php_http.c index f61357128bfc1..29a918fb8a2cb 100644 --- a/ext/soap/php_http.c +++ b/ext/soap/php_http.c @@ -19,7 +19,7 @@ #include "php_soap.h" #include "ext/standard/base64.h" #include "ext/standard/md5.h" -#include "ext/standard/php_random.h" +#include "ext/random/php_random.h" static char *get_http_header_value_nodup(char *headers, char *type, size_t *len); static char *get_http_header_value(char *headers, char *type); diff --git a/ext/spl/php_spl.c b/ext/spl/php_spl.c index a1ae90367a22c..bc53daca61a7d 100644 --- a/ext/spl/php_spl.c +++ b/ext/spl/php_spl.c @@ -36,7 +36,7 @@ #include "spl_heap.h" #include "zend_exceptions.h" #include "zend_interfaces.h" -#include "ext/standard/php_mt_rand.h" +#include "ext/random/php_random.h" #include "main/snprintf.h" #ifdef COMPILE_DL_SPL diff --git a/ext/standard/array.c b/ext/standard/array.c index 90ba30413c013..0a0a25e0890c8 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -36,12 +36,12 @@ #include "php_array.h" #include "basic_functions.h" #include "php_string.h" -#include "php_rand.h" #include "php_math.h" #include "zend_smart_str.h" #include "zend_bitset.h" #include "zend_exceptions.h" #include "ext/spl/spl_array.h" +#include "ext/random/php_random.h" /* {{{ defines */ #define EXTR_OVERWRITE 0 @@ -2892,18 +2892,17 @@ PHP_FUNCTION(range) #undef RANGE_CHECK_DOUBLE_INIT_ARRAY #undef RANGE_CHECK_LONG_INIT_ARRAY -static void php_array_data_shuffle(zval *array) /* {{{ */ +/* {{{ php_array_data_shuffle */ +PHPAPI bool php_array_data_shuffle(const php_random_algo *algo, php_random_status *status, zval *array) /* {{{ */ { - uint32_t idx, j, n_elems; + int64_t idx, j, n_elems, rnd_idx, n_left; zval *zv, temp; HashTable *hash; - zend_long rnd_idx; - uint32_t n_left; n_elems = zend_hash_num_elements(Z_ARRVAL_P(array)); if (n_elems < 1) { - return; + return true; } hash = Z_ARRVAL_P(array); @@ -2912,7 +2911,7 @@ static void php_array_data_shuffle(zval *array) /* {{{ */ if (!HT_IS_PACKED(hash)) { if (!HT_HAS_STATIC_KEYS_ONLY(hash)) { Bucket *p = hash->arData; - uint32_t i = hash->nNumUsed; + zend_long i = hash->nNumUsed; for (; i > 0; p++, i--) { if (p->key) { @@ -2936,7 +2935,10 @@ static void php_array_data_shuffle(zval *array) /* {{{ */ } } while (--n_left) { - rnd_idx = php_mt_rand_range(0, n_left); + rnd_idx = algo->range(status, 0, n_left); + if (status->last_unsafe) { + return false; + } if (rnd_idx != n_left) { ZVAL_COPY_VALUE(&temp, &hash->arPacked[n_left]); ZVAL_COPY_VALUE(&hash->arPacked[n_left], &hash->arPacked[rnd_idx]); @@ -2944,7 +2946,7 @@ static void php_array_data_shuffle(zval *array) /* {{{ */ } } } else { - uint32_t iter_pos = zend_hash_iterators_lower_pos(hash, 0); + zend_long iter_pos = zend_hash_iterators_lower_pos(hash, 0); if (hash->nNumUsed != hash->nNumOfElements) { for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) { @@ -2961,7 +2963,10 @@ static void php_array_data_shuffle(zval *array) /* {{{ */ } } while (--n_left) { - rnd_idx = php_mt_rand_range(0, n_left); + rnd_idx = algo->range(status, 0, n_left); + if (status->last_unsafe) { + return false; + } if (rnd_idx != n_left) { ZVAL_COPY_VALUE(&temp, &hash->arPacked[n_left]); ZVAL_COPY_VALUE(&hash->arPacked[n_left], &hash->arPacked[rnd_idx]); @@ -2973,6 +2978,8 @@ static void php_array_data_shuffle(zval *array) /* {{{ */ hash->nNumUsed = n_elems; hash->nInternalPointer = 0; hash->nNextFreeElement = n_elems; + + return true; } /* }}} */ @@ -2985,7 +2992,7 @@ PHP_FUNCTION(shuffle) Z_PARAM_ARRAY_EX(array, 0, 1) ZEND_PARSE_PARAMETERS_END(); - php_array_data_shuffle(array); + php_array_data_shuffle(php_random_default_algo(), php_random_default_status(), array); RETURN_TRUE; } @@ -5799,47 +5806,42 @@ PHP_FUNCTION(array_multisort) } /* }}} */ -/* {{{ Return key/keys for random entry/entries in the array */ -PHP_FUNCTION(array_rand) +/* {{{ php_array_pick_keys */ +PHPAPI bool php_array_pick_keys(const php_random_algo *algo, php_random_status *status, zval *input, zend_long num_req, zval *retval, bool silent) { - zval *input; - zend_long num_req = 1; + HashTable *ht = Z_ARRVAL_P(input); + uint32_t num_avail = zend_hash_num_elements(ht); + zend_long i, randval; zend_string *string_key; zend_ulong num_key; - int i; - int num_avail; + zval *zv; + Bucket *b; zend_bitset bitset; int negative_bitset = 0; uint32_t bitset_len; - ALLOCA_FLAG(use_heap) - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_ARRAY(input) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(num_req) - ZEND_PARSE_PARAMETERS_END(); - - num_avail = zend_hash_num_elements(Z_ARRVAL_P(input)); + ALLOCA_FLAG(use_heap); if (num_avail == 0) { - zend_argument_value_error(1, "cannot be empty"); - RETURN_THROWS(); + if (!silent) { + zend_argument_value_error(1, "cannot be empty"); + } + return false; } if (num_req == 1) { - HashTable *ht = Z_ARRVAL_P(input); - - if ((uint32_t)num_avail < ht->nNumUsed - (ht->nNumUsed>>1)) { + if (num_avail < ht->nNumUsed - (ht->nNumUsed >> 1)) { /* If less than 1/2 of elements are used, don't sample. Instead search for a * specific offset using linear scan. */ - zend_long i = 0, randval = php_mt_rand_range(0, num_avail - 1); - ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(input), num_key, string_key) { + i = 0; + randval = algo->range(status, 0, num_avail - 1); + ZEND_HASH_FOREACH_KEY(ht, num_key, string_key) { if (i == randval) { if (string_key) { - RETURN_STR_COPY(string_key); + ZVAL_STR_COPY(retval, string_key); } else { - RETURN_LONG(num_key); + ZVAL_LONG(retval, num_key); } + return true; } i++; } ZEND_HASH_FOREACH_END(); @@ -5851,34 +5853,38 @@ PHP_FUNCTION(array_rand) * For N=10 this becomes smaller than 0.1%. */ if (HT_IS_PACKED(ht)) { do { - zend_long randval = php_mt_rand_range(0, ht->nNumUsed - 1); - zval *zv = &ht->arPacked[randval]; + randval = algo->range(status, 0, ht->nNumUsed - 1); + zv = &ht->arPacked[randval]; if (!Z_ISUNDEF_P(zv)) { - RETURN_LONG(randval); + ZVAL_LONG(retval, randval); + return true; } - } while (1); + } while (true); } else { do { - zend_long randval = php_mt_rand_range(0, ht->nNumUsed - 1); - Bucket *bucket = &ht->arData[randval]; - if (!Z_ISUNDEF(bucket->val)) { - if (bucket->key) { - RETURN_STR_COPY(bucket->key); + randval = algo->range(status, 0, ht->nNumUsed - 1); + b = &ht->arData[randval]; + if (!Z_ISUNDEF(b->val)) { + if (b->key) { + ZVAL_STR_COPY(retval, b->key); } else { - RETURN_LONG(bucket->h); + ZVAL_LONG(retval, b->h); } + return true; } - } while (1); + } while (true); } } if (num_req <= 0 || num_req > num_avail) { - zend_argument_value_error(2, "must be between 1 and the number of elements in argument #1 ($array)"); - RETURN_THROWS(); + if (!silent) { + zend_argument_value_error(2, "must be between 1 and the number of elements in argument #1 ($array)"); + } + return false; } /* Make the return value an array only if we need to pass back more than one result. */ - array_init_size(return_value, (uint32_t)num_req); + array_init_size(retval, (uint32_t) num_req); if (num_req > (num_avail >> 1)) { negative_bitset = 1; num_req = num_avail - num_req; @@ -5890,19 +5896,18 @@ PHP_FUNCTION(array_rand) i = num_req; while (i) { - zend_long randval = php_mt_rand_range(0, num_avail - 1); + randval = algo->range(status, 0, num_avail - 1); if (!zend_bitset_in(bitset, randval)) { zend_bitset_incl(bitset, randval); i--; } } - /* i = 0; */ - zend_hash_real_init_packed(Z_ARRVAL_P(return_value)); - ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) { + zend_hash_real_init_packed(Z_ARRVAL_P(retval)); + ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(retval)) { /* We can't use zend_hash_index_find() * because the array may have string keys or gaps. */ - ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(input), num_key, string_key) { + ZEND_HASH_FOREACH_KEY(ht, num_key, string_key) { if (zend_bitset_in(bitset, i) ^ negative_bitset) { if (string_key) { ZEND_HASH_FILL_SET_STR_COPY(string_key); @@ -5916,6 +5921,33 @@ PHP_FUNCTION(array_rand) } ZEND_HASH_FILL_END(); free_alloca(bitset, use_heap); + + return true; +} +/* }}} */ + +/* {{{ Return key/keys for random entry/entries in the array */ +PHP_FUNCTION(array_rand) +{ + zval *input; + zend_long num_req = 1; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ARRAY(input) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(num_req) + ZEND_PARSE_PARAMETERS_END(); + + if (!php_array_pick_keys( + php_random_default_algo(), + php_random_default_status(), + input, + num_req, + return_value, + false) + ) { + RETURN_THROWS(); + } } /* }}} */ diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index 8090de0731e1b..826c635e6dbfc 100755 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -34,7 +34,6 @@ #include "zend_operators.h" #include "ext/standard/php_dns.h" #include "ext/standard/php_uuencode.h" -#include "ext/standard/php_mt_rand.h" #include "ext/standard/crc32_x86.h" #ifdef PHP_WIN32 @@ -205,11 +204,7 @@ static void php_putenv_destructor(zval *zv) /* {{{ */ static void basic_globals_ctor(php_basic_globals *basic_globals_p) /* {{{ */ { - BG(mt_rand_is_seeded) = 0; - BG(mt_rand_mode) = MT_RAND_MT19937; BG(umask) = -1; - BG(next) = NULL; - BG(left) = -1; BG(user_tick_functions) = NULL; BG(user_filter_map) = NULL; BG(serialize_lock) = 0; @@ -365,7 +360,6 @@ PHP_MINIT_FUNCTION(basic) /* {{{ */ BASIC_MINIT_SUBMODULE(standard_filters) BASIC_MINIT_SUBMODULE(user_filters) BASIC_MINIT_SUBMODULE(password) - BASIC_MINIT_SUBMODULE(mt_rand) #ifdef ZTS BASIC_MINIT_SUBMODULE(localeconv) @@ -388,7 +382,6 @@ PHP_MINIT_FUNCTION(basic) /* {{{ */ #endif BASIC_MINIT_SUBMODULE(crypt) - BASIC_MINIT_SUBMODULE(lcg) BASIC_MINIT_SUBMODULE(dir) #ifdef HAVE_SYSLOG_H @@ -420,8 +413,6 @@ PHP_MINIT_FUNCTION(basic) /* {{{ */ # endif #endif - BASIC_MINIT_SUBMODULE(random) - BASIC_MINIT_SUBMODULE(hrtime) return SUCCESS; @@ -459,7 +450,6 @@ PHP_MSHUTDOWN_FUNCTION(basic) /* {{{ */ BASIC_MSHUTDOWN_SUBMODULE(localeconv) #endif BASIC_MSHUTDOWN_SUBMODULE(crypt) - BASIC_MSHUTDOWN_SUBMODULE(random) BASIC_MSHUTDOWN_SUBMODULE(password) return SUCCESS; @@ -521,8 +511,6 @@ PHP_RSHUTDOWN_FUNCTION(basic) /* {{{ */ tsrm_env_unlock(); #endif - BG(mt_rand_is_seeded) = 0; - if (BG(umask) != -1) { umask(BG(umask)); } diff --git a/ext/standard/basic_functions.h b/ext/standard/basic_functions.h index 434de1327b17f..34cec376e7692 100644 --- a/ext/standard/basic_functions.h +++ b/ext/standard/basic_functions.h @@ -27,6 +27,9 @@ #include "url_scanner_ex.h" +/* for MT_N */ +#include "ext/random/php_random.h" + #if defined(_WIN32) && !defined(__clang__) #include #endif @@ -51,8 +54,6 @@ PHPAPI int _php_error_log(int opt_err, const char *message, const char *opt, con PHPAPI int _php_error_log_ex(int opt_err, const char *message, size_t message_len, const char *opt, const char *headers); PHPAPI int php_prefix_varname(zval *result, zend_string *prefix, const char *var_name, size_t var_name_len, bool add_underscore); -#define MT_N (624) - /* Deprecated type aliases -- use the standard types instead */ typedef uint32_t php_uint32; typedef int32_t php_int32; @@ -82,14 +83,6 @@ typedef struct _php_basic_globals { zend_string *CurrentStatFile, *CurrentLStatFile; php_stream_statbuf ssb, lssb; - /* mt_rand.c */ - uint32_t state[MT_N+1]; /* state vector + 1 extra to not violate ANSI C */ - uint32_t *next; /* next random value is computed from here */ - int left; /* can *next++ this many times before reloading */ - - bool mt_rand_is_seeded; /* Whether mt_rand() has been seeded */ - zend_long mt_rand_mode; - /* syslog.c */ char *syslog_device; diff --git a/ext/standard/basic_functions.stub.php b/ext/standard/basic_functions.stub.php index 5a781cb971361..eb611eb873f7d 100755 --- a/ext/standard/basic_functions.stub.php +++ b/ext/standard/basic_functions.stub.php @@ -651,10 +651,6 @@ function ftok(string $filename, string $project_id): int {} function hrtime(bool $as_number = false): array|int|float|false {} -/* lcg.c */ - -function lcg_value(): float {} - /* md5.c */ /** @refcount 1 */ @@ -1664,29 +1660,6 @@ function quoted_printable_decode(string $string): string {} /** @refcount 1 */ function quoted_printable_encode(string $string): string {} -/* mt_rand.c */ - -function mt_srand(int $seed = 0, int $mode = MT_RAND_MT19937): void {} - -/** @alias mt_srand */ -function srand(int $seed = 0, int $mode = MT_RAND_MT19937): void {} - -function rand(int $min = UNKNOWN, int $max = UNKNOWN): int {} - -function mt_rand(int $min = UNKNOWN, int $max = UNKNOWN): int {} - -function mt_getrandmax(): int {} - -/** @alias mt_getrandmax */ -function getrandmax(): int {} - -/* random.c */ - -/** @refcount 1 */ -function random_bytes(int $length): string {} - -function random_int(int $min, int $max): int {} - /* soundex.c */ /** @refcount 1 */ diff --git a/ext/standard/basic_functions_arginfo.h b/ext/standard/basic_functions_arginfo.h index c59759b56ce3a..31e0e440c1403 100644 --- a/ext/standard/basic_functions_arginfo.h +++ b/ext/standard/basic_functions_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 00c1b3312b1325e4dc0624c964f5bd2096245db2 */ + * Stub hash: 2d80b5ae9dd49bb2e776fe3a03b43d339bccfca0 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_set_time_limit, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, seconds, IS_LONG, 0) @@ -681,9 +681,6 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_hrtime, 0, 0, MAY_BE_ARRAY|MAY_B ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, as_number, _IS_BOOL, 0, "false") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_lcg_value, 0, 0, IS_DOUBLE, 0) -ZEND_END_ARG_INFO() - ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_md5, 0, 1, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, string, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, binary, _IS_BOOL, 0, "false") @@ -1658,7 +1655,8 @@ ZEND_END_ARG_INFO() #define arginfo_log1p arginfo_sin -#define arginfo_pi arginfo_lcg_value +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_pi, 0, 0, IS_DOUBLE, 0) +ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_is_finite, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, num, IS_DOUBLE, 0) @@ -1821,33 +1819,6 @@ ZEND_END_ARG_INFO() #define arginfo_quoted_printable_encode arginfo_base64_encode -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_mt_srand, 0, 0, IS_VOID, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, seed, IS_LONG, 0, "0") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_LONG, 0, "MT_RAND_MT19937") -ZEND_END_ARG_INFO() - -#define arginfo_srand arginfo_mt_srand - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_rand, 0, 0, IS_LONG, 0) - ZEND_ARG_TYPE_INFO(0, min, IS_LONG, 0) - ZEND_ARG_TYPE_INFO(0, max, IS_LONG, 0) -ZEND_END_ARG_INFO() - -#define arginfo_mt_rand arginfo_rand - -#define arginfo_mt_getrandmax arginfo_ob_get_level - -#define arginfo_getrandmax arginfo_ob_get_level - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_random_bytes, 0, 1, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(0, length, IS_LONG, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_random_int, 0, 2, IS_LONG, 0) - ZEND_ARG_TYPE_INFO(0, min, IS_LONG, 0) - ZEND_ARG_TYPE_INFO(0, max, IS_LONG, 0) -ZEND_END_ARG_INFO() - #define arginfo_soundex arginfo_base64_encode ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_stream_select, 0, 4, MAY_BE_LONG|MAY_BE_FALSE) @@ -2425,7 +2396,6 @@ ZEND_FUNCTION(net_get_interfaces); ZEND_FUNCTION(ftok); #endif ZEND_FUNCTION(hrtime); -ZEND_FUNCTION(lcg_value); ZEND_FUNCTION(md5); ZEND_FUNCTION(md5_file); ZEND_FUNCTION(getmyuid); @@ -2741,12 +2711,6 @@ ZEND_FUNCTION(proc_get_status); #endif ZEND_FUNCTION(quoted_printable_decode); ZEND_FUNCTION(quoted_printable_encode); -ZEND_FUNCTION(mt_srand); -ZEND_FUNCTION(rand); -ZEND_FUNCTION(mt_rand); -ZEND_FUNCTION(mt_getrandmax); -ZEND_FUNCTION(random_bytes); -ZEND_FUNCTION(random_int); ZEND_FUNCTION(soundex); ZEND_FUNCTION(stream_select); ZEND_FUNCTION(stream_context_create); @@ -3064,7 +3028,6 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(ftok, arginfo_ftok) #endif ZEND_FE(hrtime, arginfo_hrtime) - ZEND_FE(lcg_value, arginfo_lcg_value) ZEND_FE(md5, arginfo_md5) ZEND_FE(md5_file, arginfo_md5_file) ZEND_FE(getmyuid, arginfo_getmyuid) @@ -3386,14 +3349,6 @@ static const zend_function_entry ext_functions[] = { #endif ZEND_FE(quoted_printable_decode, arginfo_quoted_printable_decode) ZEND_FE(quoted_printable_encode, arginfo_quoted_printable_encode) - ZEND_FE(mt_srand, arginfo_mt_srand) - ZEND_FALIAS(srand, mt_srand, arginfo_srand) - ZEND_FE(rand, arginfo_rand) - ZEND_FE(mt_rand, arginfo_mt_rand) - ZEND_FE(mt_getrandmax, arginfo_mt_getrandmax) - ZEND_FALIAS(getrandmax, mt_getrandmax, arginfo_getrandmax) - ZEND_FE(random_bytes, arginfo_random_bytes) - ZEND_FE(random_int, arginfo_random_int) ZEND_FE(soundex, arginfo_soundex) ZEND_FE(stream_select, arginfo_stream_select) ZEND_FE(stream_context_create, arginfo_stream_context_create) diff --git a/ext/standard/config.m4 b/ext/standard/config.m4 index 87bee180a5c36..fb9d8e5415871 100644 --- a/ext/standard/config.m4 +++ b/ext/standard/config.m4 @@ -458,15 +458,15 @@ dnl PHP_NEW_EXTENSION(standard, array.c base64.c basic_functions.c browscap.c crc32.c crypt.c \ datetime.c dir.c dl.c dns.c exec.c file.c filestat.c \ flock_compat.c formatted_print.c fsock.c head.c html.c image.c \ - info.c iptc.c lcg.c link.c mail.c math.c md5.c metaphone.c \ - microtime.c pack.c pageinfo.c quot_print.c rand.c mt_rand.c \ + info.c iptc.c link.c mail.c math.c md5.c metaphone.c \ + microtime.c pack.c pageinfo.c quot_print.c \ soundex.c string.c scanf.c syslog.c type.c uniqid.c url.c \ var.c versioning.c assert.c strnatcmp.c levenshtein.c \ incomplete_class.c url_scanner_ex.c ftp_fopen_wrapper.c \ http_fopen_wrapper.c php_fopen_wrapper.c credits.c css.c \ var_unserializer.c ftok.c sha1.c user_filters.c uuencode.c \ filters.c proc_open.c streamsfuncs.c http.c password.c \ - random.c net.c hrtime.c crc32_x86.c libavifinfo/avifinfo.c,,, + net.c hrtime.c crc32_x86.c libavifinfo/avifinfo.c,,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1) PHP_ADD_BUILD_DIR($ext_builddir/libavifinfo) diff --git a/ext/standard/config.w32 b/ext/standard/config.w32 index 1813210849344..e4c5d65f4acb3 100644 --- a/ext/standard/config.w32 +++ b/ext/standard/config.w32 @@ -28,14 +28,14 @@ EXTENSION("standard", "array.c base64.c basic_functions.c browscap.c \ crypt_sha512.c php_crypt_r.c crc32_x86.c \ datetime.c dir.c dl.c dns.c dns_win32.c exec.c \ file.c filestat.c formatted_print.c fsock.c head.c html.c image.c \ - info.c iptc.c lcg.c link.c mail.c math.c md5.c metaphone.c microtime.c \ - net.c pack.c pageinfo.c quot_print.c rand.c mt_rand.c soundex.c \ + info.c iptc.c link.c mail.c math.c md5.c metaphone.c microtime.c \ + net.c pack.c pageinfo.c quot_print.c soundex.c \ string.c scanf.c syslog.c type.c uniqid.c url.c var.c \ versioning.c assert.c strnatcmp.c levenshtein.c incomplete_class.c \ url_scanner_ex.c ftp_fopen_wrapper.c http_fopen_wrapper.c \ php_fopen_wrapper.c credits.c css.c var_unserializer.c ftok.c sha1.c \ user_filters.c uuencode.c filters.c proc_open.c password.c \ - streamsfuncs.c http.c flock_compat.c random.c hrtime.c", false /* never shared */, + streamsfuncs.c http.c flock_compat.c hrtime.c", false /* never shared */, '/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1'); ADD_SOURCES("ext/standard/libavifinfo", "avifinfo.c", "standard"); PHP_STANDARD = "yes"; diff --git a/ext/standard/crypt.c b/ext/standard/crypt.c index fa18dd316e889..db3a1d33c3f05 100644 --- a/ext/standard/crypt.c +++ b/ext/standard/crypt.c @@ -43,7 +43,7 @@ #endif #include "php_crypt.h" -#include "php_random.h" +#include "ext/random/php_random.h" /* sha512 crypt has the maximal salt length of 123 characters */ #define PHP_MAX_SALT_LEN 123 diff --git a/ext/standard/lcg.c b/ext/standard/lcg.c deleted file mode 100644 index 397844844e345..0000000000000 --- a/ext/standard/lcg.c +++ /dev/null @@ -1,120 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | https://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Author: Sascha Schumann | - +----------------------------------------------------------------------+ -*/ - -#include "php.h" -#include "php_lcg.h" - -#ifdef HAVE_UNISTD_H -#include -#endif - -#ifdef PHP_WIN32 -#include "win32/time.h" -#else -#include -#endif - -#ifdef ZTS -int lcg_globals_id; -#else -static php_lcg_globals lcg_globals; -#endif - -#ifdef PHP_WIN32 -#include -#endif - -/* - * combinedLCG() returns a pseudo random number in the range of (0, 1). - * The function combines two CGs with periods of - * 2^31 - 85 and 2^31 - 249. The period of this function - * is equal to the product of both primes. - */ - -#define MODMULT(a, b, c, m, s) q = s/a;s=b*(s-a*q)-c*q;if(s<0)s+=m - -static void lcg_seed(void); - -PHPAPI double php_combined_lcg(void) /* {{{ */ -{ - int32_t q; - int32_t z; - - if (!LCG(seeded)) { - lcg_seed(); - } - - MODMULT(53668, 40014, 12211, 2147483563L, LCG(s1)); - MODMULT(52774, 40692, 3791, 2147483399L, LCG(s2)); - - z = LCG(s1) - LCG(s2); - if (z < 1) { - z += 2147483562; - } - - return z * 4.656613e-10; -} -/* }}} */ - -static void lcg_seed(void) /* {{{ */ -{ - struct timeval tv; - - if (gettimeofday(&tv, NULL) == 0) { - LCG(s1) = tv.tv_sec ^ (tv.tv_usec<<11); - } else { - LCG(s1) = 1; - } -#ifdef ZTS - LCG(s2) = (zend_long) tsrm_thread_id(); -#else - LCG(s2) = (zend_long) getpid(); -#endif - - /* Add entropy to s2 by calling gettimeofday() again */ - if (gettimeofday(&tv, NULL) == 0) { - LCG(s2) ^= (tv.tv_usec<<11); - } - - LCG(seeded) = 1; -} -/* }}} */ - -static void lcg_init_globals(php_lcg_globals *lcg_globals_p) /* {{{ */ -{ - LCG(seeded) = 0; -} -/* }}} */ - -PHP_MINIT_FUNCTION(lcg) /* {{{ */ -{ -#ifdef ZTS - ts_allocate_id(&lcg_globals_id, sizeof(php_lcg_globals), (ts_allocate_ctor) lcg_init_globals, NULL); -#else - lcg_init_globals(&lcg_globals); -#endif - return SUCCESS; -} -/* }}} */ - -/* {{{ Returns a value from the combined linear congruential generator */ -PHP_FUNCTION(lcg_value) -{ - ZEND_PARSE_PARAMETERS_NONE(); - - RETURN_DOUBLE(php_combined_lcg()); -} -/* }}} */ diff --git a/ext/standard/mt_rand.c b/ext/standard/mt_rand.c deleted file mode 100644 index d1478af1bf229..0000000000000 --- a/ext/standard/mt_rand.c +++ /dev/null @@ -1,356 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | https://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Rasmus Lerdorf | - | Zeev Suraski | - | Pedro Melo | - | Sterling Hughes | - | | - | Based on code from: Richard J. Wagner | - | Makoto Matsumoto | - | Takuji Nishimura | - | Shawn Cokus | - +----------------------------------------------------------------------+ - */ - -#include "php.h" -#include "php_rand.h" -#include "php_random.h" -#include "php_mt_rand.h" - -/* MT RAND FUNCTIONS */ - -/* - The following php_mt_...() functions are based on a C++ class MTRand by - Richard J. Wagner. For more information see the web page at - http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/VERSIONS/C-LANG/MersenneTwister.h - - Mersenne Twister random number generator -- a C++ class MTRand - Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus - Richard J. Wagner v1.0 15 May 2003 rjwagner@writeme.com - - The Mersenne Twister is an algorithm for generating random numbers. It - was designed with consideration of the flaws in various other generators. - The period, 2^19937-1, and the order of equidistribution, 623 dimensions, - are far greater. The generator is also fast; it avoids multiplication and - division, and it benefits from caches and pipelines. For more information - see the inventors' web page at http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html - - Reference - M. Matsumoto and T. Nishimura, "Mersenne Twister: A 623-Dimensionally - Equidistributed Uniform Pseudo-Random Number Generator", ACM Transactions on - Modeling and Computer Simulation, Vol. 8, No. 1, January 1998, pp 3-30. - - Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, - Copyright (C) 2000 - 2003, Richard J. Wagner - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - 3. The names of its contributors may not be used to endorse or promote - products derived from this software without specific prior written - permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#define N MT_N /* length of state vector */ -#define M (397) /* a period parameter */ -#define hiBit(u) ((u) & 0x80000000U) /* mask all but highest bit of u */ -#define loBit(u) ((u) & 0x00000001U) /* mask all but lowest bit of u */ -#define loBits(u) ((u) & 0x7FFFFFFFU) /* mask the highest bit of u */ -#define mixBits(u, v) (hiBit(u)|loBits(v)) /* move hi bit of u to hi bit of v */ - -#define twist(m,u,v) (m ^ (mixBits(u,v)>>1) ^ ((uint32_t)(-(int32_t)(loBit(v))) & 0x9908b0dfU)) -#define twist_php(m,u,v) (m ^ (mixBits(u,v)>>1) ^ ((uint32_t)(-(int32_t)(loBit(u))) & 0x9908b0dfU)) - -/* {{{ php_mt_initialize */ -static inline void php_mt_initialize(uint32_t seed, uint32_t *state) -{ - /* Initialize generator state with seed - See Knuth TAOCP Vol 2, 3rd Ed, p.106 for multiplier. - In previous versions, most significant bits (MSBs) of the seed affect - only MSBs of the state array. Modified 9 Jan 2002 by Makoto Matsumoto. */ - - uint32_t *s = state; - uint32_t *r = state; - int i = 1; - - *s++ = seed & 0xffffffffU; - for( ; i < N; ++i ) { - *s++ = ( 1812433253U * ( *r ^ (*r >> 30) ) + i ) & 0xffffffffU; - r++; - } -} -/* }}} */ - -/* {{{ php_mt_reload */ -static inline void php_mt_reload(void) -{ - /* Generate N new values in state - Made clearer and faster by Matthew Bellew (matthew.bellew@home.com) */ - - uint32_t *state = BG(state); - uint32_t *p = state; - int i; - - if (BG(mt_rand_mode) == MT_RAND_MT19937) { - for (i = N - M; i--; ++p) - *p = twist(p[M], p[0], p[1]); - for (i = M; --i; ++p) - *p = twist(p[M-N], p[0], p[1]); - *p = twist(p[M-N], p[0], state[0]); - } - else { - for (i = N - M; i--; ++p) - *p = twist_php(p[M], p[0], p[1]); - for (i = M; --i; ++p) - *p = twist_php(p[M-N], p[0], p[1]); - *p = twist_php(p[M-N], p[0], state[0]); - } - BG(left) = N; - BG(next) = state; -} -/* }}} */ - -/* {{{ php_mt_srand */ -PHPAPI void php_mt_srand(uint32_t seed) -{ - /* Seed the generator with a simple uint32 */ - php_mt_initialize(seed, BG(state)); - php_mt_reload(); - - /* Seed only once */ - BG(mt_rand_is_seeded) = 1; -} -/* }}} */ - -/* {{{ php_mt_rand */ -PHPAPI uint32_t php_mt_rand(void) -{ - /* Pull a 32-bit integer from the generator state - Every other access function simply transforms the numbers extracted here */ - - uint32_t s1; - - if (UNEXPECTED(!BG(mt_rand_is_seeded))) { - zend_long bytes; - if (php_random_bytes_silent(&bytes, sizeof(zend_long)) == FAILURE) { - bytes = GENERATE_SEED(); - } - php_mt_srand(bytes); - } - - if (BG(left) == 0) { - php_mt_reload(); - } - --BG(left); - - s1 = *BG(next)++; - s1 ^= (s1 >> 11); - s1 ^= (s1 << 7) & 0x9d2c5680U; - s1 ^= (s1 << 15) & 0xefc60000U; - return ( s1 ^ (s1 >> 18) ); -} -/* }}} */ - -/* {{{ Seeds Mersenne Twister random number generator */ -PHP_FUNCTION(mt_srand) -{ - zend_long seed = 0; - zend_long mode = MT_RAND_MT19937; - - ZEND_PARSE_PARAMETERS_START(0, 2) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(seed) - Z_PARAM_LONG(mode) - ZEND_PARSE_PARAMETERS_END(); - - if (ZEND_NUM_ARGS() == 0) { - if (php_random_bytes_silent(&seed, sizeof(zend_long)) == FAILURE) { - seed = GENERATE_SEED(); - } - } - - switch (mode) { - case MT_RAND_PHP: - BG(mt_rand_mode) = MT_RAND_PHP; - break; - default: - BG(mt_rand_mode) = MT_RAND_MT19937; - } - - php_mt_srand(seed); -} -/* }}} */ - -static uint32_t rand_range32(uint32_t umax) { - uint32_t result, limit; - - result = php_mt_rand(); - - /* Special case where no modulus is required */ - if (UNEXPECTED(umax == UINT32_MAX)) { - return result; - } - - /* Increment the max so the range is inclusive of max */ - umax++; - - /* Powers of two are not biased */ - if ((umax & (umax - 1)) == 0) { - return result & (umax - 1); - } - - /* Ceiling under which UINT32_MAX % max == 0 */ - limit = UINT32_MAX - (UINT32_MAX % umax) - 1; - - /* Discard numbers over the limit to avoid modulo bias */ - while (UNEXPECTED(result > limit)) { - result = php_mt_rand(); - } - - return result % umax; -} - -#if ZEND_ULONG_MAX > UINT32_MAX -static uint64_t rand_range64(uint64_t umax) { - uint64_t result, limit; - - result = php_mt_rand(); - result = (result << 32) | php_mt_rand(); - - /* Special case where no modulus is required */ - if (UNEXPECTED(umax == UINT64_MAX)) { - return result; - } - - /* Increment the max so the range is inclusive of max */ - umax++; - - /* Powers of two are not biased */ - if ((umax & (umax - 1)) == 0) { - return result & (umax - 1); - } - - /* Ceiling under which UINT64_MAX % max == 0 */ - limit = UINT64_MAX - (UINT64_MAX % umax) - 1; - - /* Discard numbers over the limit to avoid modulo bias */ - while (UNEXPECTED(result > limit)) { - result = php_mt_rand(); - result = (result << 32) | php_mt_rand(); - } - - return result % umax; -} -#endif - -/* {{{ php_mt_rand_range */ -PHPAPI zend_long php_mt_rand_range(zend_long min, zend_long max) -{ - zend_ulong umax = max - min; - -#if ZEND_ULONG_MAX > UINT32_MAX - if (umax > UINT32_MAX) { - return (zend_long) (rand_range64(umax) + min); - } -#endif - - return (zend_long) (rand_range32(umax) + min); -} -/* }}} */ - -/* {{{ php_mt_rand_common - * rand() allows min > max, mt_rand does not */ -PHPAPI zend_long php_mt_rand_common(zend_long min, zend_long max) -{ - int64_t n; - - if (BG(mt_rand_mode) == MT_RAND_MT19937) { - return php_mt_rand_range(min, max); - } - - /* Legacy mode deliberately not inside php_mt_rand_range() - * to prevent other functions being affected */ - n = (int64_t)php_mt_rand() >> 1; - RAND_RANGE_BADSCALING(n, min, max, PHP_MT_RAND_MAX); - - return n; -} -/* }}} */ - -/* {{{ Returns a random number from Mersenne Twister */ -PHP_FUNCTION(mt_rand) -{ - zend_long min; - zend_long max; - int argc = ZEND_NUM_ARGS(); - - if (argc == 0) { - // genrand_int31 in mt19937ar.c performs a right shift - RETURN_LONG(php_mt_rand() >> 1); - } - - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_LONG(min) - Z_PARAM_LONG(max) - ZEND_PARSE_PARAMETERS_END(); - - if (UNEXPECTED(max < min)) { - zend_argument_value_error(2, "must be greater than or equal to argument #1 ($min)"); - RETURN_THROWS(); - } - - RETURN_LONG(php_mt_rand_common(min, max)); -} -/* }}} */ - -/* {{{ Returns the maximum value a random number from Mersenne Twister can have */ -PHP_FUNCTION(mt_getrandmax) -{ - ZEND_PARSE_PARAMETERS_NONE(); - - /* - * Melo: it could be 2^^32 but we only use 2^^31 to maintain - * compatibility with the previous php_rand - */ - RETURN_LONG(PHP_MT_RAND_MAX); /* 2^^31 */ -} -/* }}} */ - -PHP_MINIT_FUNCTION(mt_rand) -{ - REGISTER_LONG_CONSTANT("MT_RAND_MT19937", MT_RAND_MT19937, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("MT_RAND_PHP", MT_RAND_PHP, CONST_CS | CONST_PERSISTENT); - - return SUCCESS; -} diff --git a/ext/standard/password.c b/ext/standard/password.c index 374b026562842..81117f17b5bcd 100644 --- a/ext/standard/password.c +++ b/ext/standard/password.c @@ -21,12 +21,11 @@ #include "fcntl.h" #include "php_password.h" -#include "php_rand.h" #include "php_crypt.h" #include "base64.h" #include "zend_interfaces.h" #include "info.h" -#include "php_random.h" +#include "ext/random/php_random.h" #ifdef HAVE_ARGON2LIB #include "argon2.h" #endif diff --git a/ext/standard/php_array.h b/ext/standard/php_array.h index c11f769205994..cac6a44ce91e4 100644 --- a/ext/standard/php_array.h +++ b/ext/standard/php_array.h @@ -20,6 +20,8 @@ #ifndef PHP_ARRAY_H #define PHP_ARRAY_H +# include "ext/random/php_random.h" + PHP_MINIT_FUNCTION(array); PHP_MSHUTDOWN_FUNCTION(array); @@ -29,6 +31,9 @@ PHPAPI int php_array_replace_recursive(HashTable *dest, HashTable *src); PHPAPI int php_multisort_compare(const void *a, const void *b); PHPAPI zend_long php_count_recursive(HashTable *ht); +PHPAPI bool php_array_data_shuffle(const php_random_algo *algo, php_random_status *status, zval *array); +PHPAPI bool php_array_pick_keys(const php_random_algo *algo, php_random_status *status, zval *input, zend_long num_req, zval *retval, bool silent); + #define PHP_SORT_REGULAR 0 #define PHP_SORT_NUMERIC 1 #define PHP_SORT_STRING 2 diff --git a/ext/standard/php_lcg.h b/ext/standard/php_lcg.h index b18d422cf9de1..b86bb003d37dc 100644 --- a/ext/standard/php_lcg.h +++ b/ext/standard/php_lcg.h @@ -1,38 +1 @@ -/* - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | https://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Author: Sascha Schumann | - +----------------------------------------------------------------------+ -*/ - -#ifndef PHP_LCG_H -#define PHP_LCG_H - -#include "ext/standard/basic_functions.h" - -typedef struct { - int32_t s1; - int32_t s2; - int seeded; -} php_lcg_globals; - -PHPAPI double php_combined_lcg(void); - -PHP_MINIT_FUNCTION(lcg); - -#ifdef ZTS -#define LCG(v) ZEND_TSRMG(lcg_globals_id, php_lcg_globals *, v) -#else -#define LCG(v) (lcg_globals.v) -#endif - -#endif +#include "ext/random/php_random.h" diff --git a/ext/standard/php_mt_rand.h b/ext/standard/php_mt_rand.h index 169c2a23332b8..b86bb003d37dc 100644 --- a/ext/standard/php_mt_rand.h +++ b/ext/standard/php_mt_rand.h @@ -1,40 +1 @@ -/* - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | https://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Rasmus Lerdorf | - | Zeev Suraski | - | Pedro Melo | - | Sterling Hughes | - | | - | Based on code from: Shawn Cokus | - +----------------------------------------------------------------------+ - */ - -#ifndef PHP_MT_RAND_H -#define PHP_MT_RAND_H - -#include "php_lcg.h" -#include "php_rand.h" - -#define PHP_MT_RAND_MAX ((zend_long) (0x7FFFFFFF)) /* (1<<31) - 1 */ - -#define MT_RAND_MT19937 0 -#define MT_RAND_PHP 1 - -PHPAPI void php_mt_srand(uint32_t seed); -PHPAPI uint32_t php_mt_rand(void); -PHPAPI zend_long php_mt_rand_range(zend_long min, zend_long max); -PHPAPI zend_long php_mt_rand_common(zend_long min, zend_long max); - -PHP_MINIT_FUNCTION(mt_rand); - -#endif /* PHP_MT_RAND_H */ +#include "ext/random/php_random.h" diff --git a/ext/standard/php_rand.h b/ext/standard/php_rand.h index e8018722772f7..b86bb003d37dc 100644 --- a/ext/standard/php_rand.h +++ b/ext/standard/php_rand.h @@ -1,72 +1 @@ -/* - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | https://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Rasmus Lerdorf | - | Zeev Suraski | - | Pedro Melo | - | Sterling Hughes | - | | - | Based on code from: Shawn Cokus | - +----------------------------------------------------------------------+ - */ - -#ifndef PHP_RAND_H -#define PHP_RAND_H - -#include "php_lcg.h" -#include "php_mt_rand.h" - -/* System Rand functions */ -#ifndef RAND_MAX -#define RAND_MAX PHP_MT_RAND_MAX -#endif - -#define PHP_RAND_MAX PHP_MT_RAND_MAX - -/* - * A bit of tricky math here. We want to avoid using a modulus because - * that simply tosses the high-order bits and might skew the distribution - * of random values over the range. Instead we map the range directly. - * - * We need to map the range from 0...M evenly to the range a...b - * Let n = the random number and n' = the mapped random number - * - * Then we have: n' = a + n(b-a)/M - * - * We have a problem here in that only n==M will get mapped to b which - # means the chances of getting b is much much less than getting any of - # the other values in the range. We can fix this by increasing our range - # artificially and using: - # - # n' = a + n(b-a+1)/M - * - # Now we only have a problem if n==M which would cause us to produce a - # number of b+1 which would be bad. So we bump M up by one to make sure - # this will never happen, and the final algorithm looks like this: - # - # n' = a + n(b-a+1)/(M+1) - * - * -RL - */ -#define RAND_RANGE_BADSCALING(__n, __min, __max, __tmax) \ - (__n) = (__min) + (zend_long) ((double) ( (double) (__max) - (__min) + 1.0) * ((__n) / ((__tmax) + 1.0))) - -#ifdef PHP_WIN32 -#define GENERATE_SEED() (((zend_long) (time(0) * GetCurrentProcessId())) ^ ((zend_long) (1000000.0 * php_combined_lcg()))) -#else -#define GENERATE_SEED() (((zend_long) (time(0) * getpid())) ^ ((zend_long) (1000000.0 * php_combined_lcg()))) -#endif - -PHPAPI void php_srand(zend_long seed); -PHPAPI zend_long php_rand(void); - -#endif /* PHP_RAND_H */ +#include "ext/random/php_random.h" diff --git a/ext/standard/php_random.h b/ext/standard/php_random.h index b4b492c6af6ab..9af3eef3fd92e 100644 --- a/ext/standard/php_random.h +++ b/ext/standard/php_random.h @@ -1,50 +1 @@ -/* - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | https://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Sammy Kaye Powers | - +----------------------------------------------------------------------+ -*/ - -#ifndef PHP_RANDOM_H -#define PHP_RANDOM_H - -BEGIN_EXTERN_C() - -PHP_MINIT_FUNCTION(random); -PHP_MSHUTDOWN_FUNCTION(random); - -typedef struct { - int fd; -} php_random_globals; - -#define php_random_bytes_throw(b, s) php_random_bytes((b), (s), 1) -#define php_random_bytes_silent(b, s) php_random_bytes((b), (s), 0) - -#define php_random_int_throw(min, max, result) \ - php_random_int((min), (max), (result), 1) -#define php_random_int_silent(min, max, result) \ - php_random_int((min), (max), (result), 0) - -PHPAPI int php_random_bytes(void *bytes, size_t size, bool should_throw); -PHPAPI int php_random_int(zend_long min, zend_long max, zend_long *result, bool should_throw); - -#ifdef ZTS -# define RANDOM_G(v) ZEND_TSRMG(random_globals_id, php_random_globals *, v) -extern PHPAPI int random_globals_id; -#else -# define RANDOM_G(v) random_globals.v -extern PHPAPI php_random_globals random_globals; -#endif - -END_EXTERN_C() - -#endif +#include "ext/random/php_random.h" \ No newline at end of file diff --git a/ext/standard/php_standard.h b/ext/standard/php_standard.h index cb948ae6b2870..5db7ebf38f057 100644 --- a/ext/standard/php_standard.h +++ b/ext/standard/php_standard.h @@ -42,13 +42,11 @@ #include "dl.h" #include "php_crypt.h" #include "head.h" -#include "php_lcg.h" #include "php_output.h" #include "php_array.h" #include "php_assert.h" #include "php_versioning.h" #include "php_password.h" -#include "php_random.h" #include "php_version.h" #define PHP_STANDARD_VERSION PHP_VERSION diff --git a/ext/standard/php_string.h b/ext/standard/php_string.h index b87601a1241ed..3759e61a6ac32 100644 --- a/ext/standard/php_string.h +++ b/ext/standard/php_string.h @@ -18,6 +18,8 @@ #ifndef PHP_STRING_H #define PHP_STRING_H +# include "ext/random/php_random.h" + #ifdef ZTS PHP_MINIT_FUNCTION(localeconv); PHP_MSHUTDOWN_FUNCTION(localeconv); @@ -59,6 +61,12 @@ PHPAPI void php_explode(const zend_string *delim, zend_string *str, zval *return PHPAPI size_t php_strspn(const char *s1, const char *s2, const char *s1_end, const char *s2_end); PHPAPI size_t php_strcspn(const char *s1, const char *s2, const char *s1_end, const char *s2_end); +PHPAPI int string_natural_compare_function_ex(zval *result, zval *op1, zval *op2, bool case_insensitive); +PHPAPI int string_natural_compare_function(zval *result, zval *op1, zval *op2); +PHPAPI int string_natural_case_compare_function(zval *result, zval *op1, zval *op2); + +PHPAPI bool php_binary_string_shuffle(const php_random_algo *algo, php_random_status *status, char *str, zend_long len); + #ifdef _REENTRANT # ifdef PHP_WIN32 # include diff --git a/ext/standard/rand.c b/ext/standard/rand.c deleted file mode 100644 index 9824e416cfa21..0000000000000 --- a/ext/standard/rand.c +++ /dev/null @@ -1,65 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | https://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Rasmus Lerdorf | - | Zeev Suraski | - | Pedro Melo | - | Sterling Hughes | - | | - | Based on code from: Richard J. Wagner | - | Makoto Matsumoto | - | Takuji Nishimura | - | Shawn Cokus | - +----------------------------------------------------------------------+ - */ - -#include "php.h" -#include "php_rand.h" -#include "php_mt_rand.h" - -/* {{{ php_srand */ -PHPAPI void php_srand(zend_long seed) -{ - php_mt_srand(seed); -} -/* }}} */ - -/* {{{ php_rand */ -PHPAPI zend_long php_rand(void) -{ - return php_mt_rand(); -} -/* }}} */ - -/* {{{ Returns a random number from Mersenne Twister */ -PHP_FUNCTION(rand) -{ - zend_long min; - zend_long max; - int argc = ZEND_NUM_ARGS(); - - if (argc == 0) { - RETURN_LONG(php_mt_rand() >> 1); - } - - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_LONG(min) - Z_PARAM_LONG(max) - ZEND_PARSE_PARAMETERS_END(); - - if (max < min) { - RETURN_LONG(php_mt_rand_common(max, min)); - } - - RETURN_LONG(php_mt_rand_common(min, max)); -} -/* }}} */ diff --git a/ext/standard/random.c b/ext/standard/random.c deleted file mode 100644 index f971d8344721d..0000000000000 --- a/ext/standard/random.c +++ /dev/null @@ -1,308 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | https://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Sammy Kaye Powers | - +----------------------------------------------------------------------+ -*/ - -#include -#include -#include -#include - -#include "php.h" -#include "zend_exceptions.h" -#include "php_random.h" - -#ifdef PHP_WIN32 -# include "win32/winutil.h" -#endif -#ifdef __linux__ -# include -#endif -#if HAVE_SYS_PARAM_H -# include -# if (__FreeBSD__ && __FreeBSD_version > 1200000) || (__DragonFly__ && __DragonFly_version >= 500700) || defined(__sun) -# include -# endif -#endif -#if HAVE_COMMONCRYPTO_COMMONRANDOM_H -# include -# include -#endif - -#if __has_feature(memory_sanitizer) -# include -#endif - -#ifdef ZTS -int random_globals_id; -#else -php_random_globals random_globals; -#endif - -static void random_globals_ctor(php_random_globals *random_globals_p) -{ - random_globals_p->fd = -1; -} - -static void random_globals_dtor(php_random_globals *random_globals_p) -{ - if (random_globals_p->fd > 0) { - close(random_globals_p->fd); - random_globals_p->fd = -1; - } -} - -/* {{{ */ -PHP_MINIT_FUNCTION(random) -{ -#ifdef ZTS - ts_allocate_id(&random_globals_id, sizeof(php_random_globals), (ts_allocate_ctor)random_globals_ctor, (ts_allocate_dtor)random_globals_dtor); -#else - random_globals_ctor(&random_globals); -#endif - - return SUCCESS; -} -/* }}} */ - -/* {{{ */ -PHP_MSHUTDOWN_FUNCTION(random) -{ -#ifndef ZTS - random_globals_dtor(&random_globals); -#endif - - return SUCCESS; -} -/* }}} */ - -/* {{{ php_random_bytes */ -PHPAPI int php_random_bytes(void *bytes, size_t size, bool should_throw) -{ -#ifdef PHP_WIN32 - /* Defer to CryptGenRandom on Windows */ - if (php_win32_get_random_bytes(bytes, size) == FAILURE) { - if (should_throw) { - zend_throw_exception(zend_ce_exception, "Could not gather sufficient random data", 0); - } - return FAILURE; - } -#elif HAVE_COMMONCRYPTO_COMMONRANDOM_H - /* - * Purposely prioritized upon arc4random_buf for modern macOs releases - * arc4random api on this platform uses `ccrng_generate` which returns - * a status but silented to respect the "no fail" arc4random api interface - * the vast majority of the time, it works fine ; but better make sure we catch failures - */ - if (CCRandomGenerateBytes(bytes, size) != kCCSuccess) { - if (should_throw) { - zend_throw_exception(zend_ce_exception, "Error generating bytes", 0); - } - return FAILURE; - } -#elif HAVE_DECL_ARC4RANDOM_BUF && ((defined(__OpenBSD__) && OpenBSD >= 201405) || (defined(__NetBSD__) && __NetBSD_Version__ >= 700000001) || defined(__APPLE__) || defined(__GLIBC__)) - arc4random_buf(bytes, size); -#else - size_t read_bytes = 0; - ssize_t n; -#if (defined(__linux__) && defined(SYS_getrandom)) || (defined(__FreeBSD__) && __FreeBSD_version >= 1200000) || (defined(__DragonFly__) && __DragonFly_version >= 500700) || defined(__sun) - /* Linux getrandom(2) syscall or FreeBSD/DragonFlyBSD getrandom(2) function*/ - /* Keep reading until we get enough entropy */ - while (read_bytes < size) { - /* Below, (bytes + read_bytes) is pointer arithmetic. - - bytes read_bytes size - | | | - [#######=============] (we're going to write over the = region) - \\\\\\\\\\\\\ - amount_to_read - - */ - size_t amount_to_read = size - read_bytes; -#if defined(__linux__) - n = syscall(SYS_getrandom, bytes + read_bytes, amount_to_read, 0); -#else - n = getrandom(bytes + read_bytes, amount_to_read, 0); -#endif - - if (n == -1) { - if (errno == ENOSYS) { - /* This can happen if PHP was compiled against a newer kernel where getrandom() - * is available, but then runs on an older kernel without getrandom(). If this - * happens we simply fall back to reading from /dev/urandom. */ - ZEND_ASSERT(read_bytes == 0); - break; - } else if (errno == EINTR || errno == EAGAIN) { - /* Try again */ - continue; - } else { - /* If the syscall fails, fall back to reading from /dev/urandom */ - break; - } - } - -#if __has_feature(memory_sanitizer) - /* MSan does not instrument manual syscall invocations. */ - __msan_unpoison(bytes + read_bytes, n); -#endif - read_bytes += (size_t) n; - } -#endif - if (read_bytes < size) { - int fd = RANDOM_G(fd); - struct stat st; - - if (fd < 0) { -#ifdef HAVE_DEV_URANDOM - fd = open("/dev/urandom", O_RDONLY); -#endif - if (fd < 0) { - if (should_throw) { - zend_throw_exception(zend_ce_exception, "Cannot open source device", 0); - } - return FAILURE; - } - /* Does the file exist and is it a character device? */ - if (fstat(fd, &st) != 0 || -# ifdef S_ISNAM - !(S_ISNAM(st.st_mode) || S_ISCHR(st.st_mode)) -# else - !S_ISCHR(st.st_mode) -# endif - ) { - close(fd); - if (should_throw) { - zend_throw_exception(zend_ce_exception, "Error reading from source device", 0); - } - return FAILURE; - } - RANDOM_G(fd) = fd; - } - - for (read_bytes = 0; read_bytes < size; read_bytes += (size_t) n) { - n = read(fd, bytes + read_bytes, size - read_bytes); - if (n <= 0) { - break; - } - } - - if (read_bytes < size) { - if (should_throw) { - zend_throw_exception(zend_ce_exception, "Could not gather sufficient random data", 0); - } - return FAILURE; - } - } -#endif - - return SUCCESS; -} -/* }}} */ - -/* {{{ Return an arbitrary length of pseudo-random bytes as binary string */ -PHP_FUNCTION(random_bytes) -{ - zend_long size; - zend_string *bytes; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_LONG(size) - ZEND_PARSE_PARAMETERS_END(); - - if (size < 1) { - zend_argument_value_error(1, "must be greater than 0"); - RETURN_THROWS(); - } - - bytes = zend_string_alloc(size, 0); - - if (php_random_bytes_throw(ZSTR_VAL(bytes), size) == FAILURE) { - zend_string_release_ex(bytes, 0); - RETURN_THROWS(); - } - - ZSTR_VAL(bytes)[size] = '\0'; - - RETURN_STR(bytes); -} -/* }}} */ - -/* {{{ */ -PHPAPI int php_random_int(zend_long min, zend_long max, zend_long *result, bool should_throw) -{ - zend_ulong umax; - zend_ulong trial; - - if (min == max) { - *result = min; - return SUCCESS; - } - - umax = (zend_ulong) max - (zend_ulong) min; - - if (php_random_bytes(&trial, sizeof(trial), should_throw) == FAILURE) { - return FAILURE; - } - - /* Special case where no modulus is required */ - if (umax == ZEND_ULONG_MAX) { - *result = (zend_long)trial; - return SUCCESS; - } - - /* Increment the max so the range is inclusive of max */ - umax++; - - /* Powers of two are not biased */ - if ((umax & (umax - 1)) != 0) { - /* Ceiling under which ZEND_LONG_MAX % max == 0 */ - zend_ulong limit = ZEND_ULONG_MAX - (ZEND_ULONG_MAX % umax) - 1; - - /* Discard numbers over the limit to avoid modulo bias */ - while (trial > limit) { - if (php_random_bytes(&trial, sizeof(trial), should_throw) == FAILURE) { - return FAILURE; - } - } - } - - *result = (zend_long)((trial % umax) + min); - return SUCCESS; -} -/* }}} */ - -/* {{{ Return an arbitrary pseudo-random integer */ -PHP_FUNCTION(random_int) -{ - zend_long min; - zend_long max; - zend_long result; - - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_LONG(min) - Z_PARAM_LONG(max) - ZEND_PARSE_PARAMETERS_END(); - - if (min > max) { - zend_argument_value_error(1, "must be less than or equal to argument #2 ($max)"); - RETURN_THROWS(); - } - - if (php_random_int_throw(min, max, &result) == FAILURE) { - RETURN_THROWS(); - } - - RETURN_LONG(result); -} -/* }}} */ diff --git a/ext/standard/string.c b/ext/standard/string.c index e247b4b08f5c8..faeeebd2c5b8f 100644 --- a/ext/standard/string.c +++ b/ext/standard/string.c @@ -18,7 +18,6 @@ #include #include "php.h" -#include "php_rand.h" #include "php_string.h" #include "php_variables.h" #include @@ -45,6 +44,7 @@ #include "ext/standard/file.h" /* For php_next_utf8_char() */ #include "ext/standard/html.h" +#include "ext/random/php_random.h" #ifdef __SSE2__ #include @@ -5737,28 +5737,35 @@ PHP_FUNCTION(str_rot13) } /* }}} */ -static void php_string_shuffle(char *str, size_t len) /* {{{ */ +/* {{{ php_binary_string_shuffle */ +PHPAPI bool php_binary_string_shuffle(const php_random_algo *algo, php_random_status *status, char *str, zend_long len) /* {{{ */ { - zend_long n_elems, rnd_idx, n_left; + int64_t n_elems, rnd_idx, n_left; char temp; + /* The implementation is stolen from array_data_shuffle */ /* Thus the characteristics of the randomization are the same */ n_elems = len; if (n_elems <= 1) { - return; + return true; } n_left = n_elems; while (--n_left) { - rnd_idx = php_mt_rand_range(0, n_left); + rnd_idx = algo->range(status, 0, n_left); + if (status->last_unsafe) { + return false; + } if (rnd_idx != n_left) { temp = str[n_left]; str[n_left] = str[rnd_idx]; str[rnd_idx] = temp; } } + + return true; } /* }}} */ @@ -5773,7 +5780,12 @@ PHP_FUNCTION(str_shuffle) RETVAL_STRINGL(ZSTR_VAL(arg), ZSTR_LEN(arg)); if (Z_STRLEN_P(return_value) > 1) { - php_string_shuffle(Z_STRVAL_P(return_value), Z_STRLEN_P(return_value)); + php_binary_string_shuffle( + php_random_default_algo(), + php_random_default_status(), + Z_STRVAL_P(return_value), + Z_STRLEN_P(return_value) + ); } } /* }}} */ diff --git a/ext/standard/uniqid.c b/ext/standard/uniqid.c index a28f30e969a14..c6a68ed3b827d 100644 --- a/ext/standard/uniqid.c +++ b/ext/standard/uniqid.c @@ -31,8 +31,7 @@ #include #endif -#include "php_lcg.h" -#include "php_random.h" +#include "ext/random/php_random.h" #ifdef HAVE_GETTIMEOFDAY ZEND_TLS struct timeval prev_tv = { 0, 0 }; diff --git a/main/internal_functions_win32.c b/main/internal_functions_win32.c index eebafc43d34fa..6c7bc0fef493b 100644 --- a/main/internal_functions_win32.c +++ b/main/internal_functions_win32.c @@ -36,10 +36,10 @@ #include "ext/standard/php_mail.h" #include "ext/standard/php_ext_syslog.h" #include "ext/standard/php_standard.h" -#include "ext/standard/php_lcg.h" #include "ext/standard/php_array.h" #include "ext/standard/php_assert.h" #include "ext/reflection/php_reflection.h" +#include "ext/random/php_random.h" #if HAVE_BCMATH #include "ext/bcmath/php_bcmath.h" #endif diff --git a/main/reentrancy.c b/main/reentrancy.c index 8d9a128f59476..6d39a4ebd873d 100644 --- a/main/reentrancy.c +++ b/main/reentrancy.c @@ -22,7 +22,7 @@ #endif #include "php_reentrancy.h" -#include "ext/standard/php_rand.h" /* for PHP_RAND_MAX */ +#include "ext/random/php_random.h" /* for PHP_RAND_MAX */ enum { LOCALTIME_R,