diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index 01a206c0985bd..bd4efc175f5d7 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 315da803de661..61efab73b3ad3 100644 --- a/ext/gmp/gmp.c +++ b/ext/gmp/gmp.c @@ -31,8 +31,7 @@ #include "gmp_arginfo.h" /* Needed for gmp_random() */ -#include "ext/standard/php_rand.h" -#include "ext/standard/php_lcg.h" +#include "ext/random/php_random.h" ZEND_DECLARE_MODULE_GLOBALS(gmp) static ZEND_GINIT_FUNCTION(gmp); diff --git a/ext/random/config.m4 b/ext/random/config.m4 new file mode 100644 index 0000000000000..be30e9064af35 --- /dev/null +++ b/ext/random/config.m4 @@ -0,0 +1,10 @@ +dnl +dnl Check for arc4random on BSD systems +dnl +AC_CHECK_DECLS([arc4random_buf]) + +dnl +dnl Setup extension +dnl +PHP_NEW_EXTENSION(random, random.c , , , -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1) +PHP_INSTALL_HEADERS([ext/random]) diff --git a/ext/random/config.w32 b/ext/random/config.w32 new file mode 100644 index 0000000000000..411a6c26aa4f4 --- /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"; +PHP_INSTALL_HEADERS("ext/random", "php_random.h"); +ADD_MAKEFILE_FRAGMENT(); diff --git a/ext/random/php_random.h b/ext/random/php_random.h new file mode 100644 index 0000000000000..13243b4dfcf16 --- /dev/null +++ b/ext/random/php_random.h @@ -0,0 +1,116 @@ +/* + +----------------------------------------------------------------------+ + | 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 | + | | + | Rand based on code from: Shawn Cokus | + +----------------------------------------------------------------------+ +*/ + +#ifndef PHP_RANDOM_H +#define PHP_RANDOM_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))) + +#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) + +extern zend_module_entry random_module_entry; +#define phpext_random_ptr &random_module_entry + +PHP_MINIT_FUNCTION(random); +PHP_MSHUTDOWN_FUNCTION(random); + +ZEND_BEGIN_MODULE_GLOBALS(random) + int32_t lcg_s1; + int32_t lcg_s2; + int lcg_seeded; + uint32_t mt_rand_state[MT_N+1]; /* state vector + 1 extra to not violate ANSI C */ + uint32_t *mt_rand_next; /* next random value is computed from here */ + int mt_rand_left; /* can *next++ this many times before reloading */ + bool mt_rand_is_seeded; /* Whether mt_rand() has been seeded */ + zend_long mt_rand_mode; + int random_fd; +ZEND_END_MODULE_GLOBALS(random) + +#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_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 double php_combined_lcg(void); + +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); + +PHPAPI void php_srand(zend_long seed); +PHPAPI zend_long php_rand(void); + +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); + +#define RANDOM_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(random, v) + +#endif diff --git a/ext/standard/mt_rand.c b/ext/random/random.c similarity index 52% rename from ext/standard/mt_rand.c rename to ext/random/random.c index d1478af1bf229..152f739f7b732 100644 --- a/ext/standard/mt_rand.c +++ b/ext/random/random.c @@ -12,20 +12,62 @@ +----------------------------------------------------------------------+ | Authors: Rasmus Lerdorf | | Zeev Suraski | + | Sascha Schumann | | Pedro Melo | | Sterling Hughes | + | Sammy Kaye Powers | | | | Based on code from: Richard J. Wagner | | Makoto Matsumoto | | Takuji Nishimura | | Shawn Cokus | +----------------------------------------------------------------------+ - */ +*/ + +#include +#include +#include +#include #include "php.h" -#include "php_rand.h" +#include "zend_exceptions.h" + #include "php_random.h" -#include "php_mt_rand.h" +#include "random_arginfo.h" + +#if HAVE_UNISTD_H +# include +#endif + +#ifdef PHP_WIN32 +# include "win32/winutil.h" +# include "win32/time.h" +# include +#else +# include +#endif +#ifdef __linux__ +# include +#endif +#if HAVE_SYS_PARAM_H +# include +# if (__FreeBSD__ && __FreeBSD_version > 1200000) || (__DragonFly__ && __DragonFly_version >= 500700) +# include +# endif +#endif + +#if __has_feature(memory_sanitizer) +# 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 /* MT RAND FUNCTIONS */ @@ -92,6 +134,69 @@ #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)) +ZEND_DECLARE_MODULE_GLOBALS(random); + +/* {{{ lcg_seed */ +static void lcg_seed(void) +{ + struct timeval tv; + + if (gettimeofday(&tv, NULL) == 0) { + RANDOM_G(lcg_s1) = tv.tv_sec ^ (tv.tv_usec<<11); + } else { + RANDOM_G(lcg_s1) = 1; + } +#ifdef ZTS + RANDOM_G(lcg_s2) = (zend_long) tsrm_thread_id(); +#else + RANDOM_G(lcg_s2) = (zend_long) getpid(); +#endif + + /* Add entropy to s2 by calling gettimeofday() again */ + if (gettimeofday(&tv, NULL) == 0) { + RANDOM_G(lcg_s2) ^= (tv.tv_usec<<11); + } + + RANDOM_G(lcg_seeded) = 1; +} +/* }}} */ + +/* {{{ php_combined_lcg */ +PHPAPI double php_combined_lcg(void) +{ + int32_t q; + int32_t z; + + if (!RANDOM_G(lcg_seeded)) { + lcg_seed(); + } + + MODMULT(53668, 40014, 12211, 2147483563L, RANDOM_G(lcg_s1)); + MODMULT(52774, 40692, 3791, 2147483399L, RANDOM_G(lcg_s2)); + + z = RANDOM_G(lcg_s1) - RANDOM_G(lcg_s2); + if (z < 1) { + z += 2147483562; + } + + return z * 4.656613e-10; +} +/* }}} */ + +/* {{{ 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(); +} +/* }}} */ + /* {{{ php_mt_initialize */ static inline void php_mt_initialize(uint32_t seed, uint32_t *state) { @@ -118,11 +223,11 @@ 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 *state = RANDOM_G(mt_rand_state); uint32_t *p = state; int i; - if (BG(mt_rand_mode) == MT_RAND_MT19937) { + if (RANDOM_G(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) @@ -136,8 +241,8 @@ static inline void php_mt_reload(void) *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; + RANDOM_G(mt_rand_left) = N; + RANDOM_G(mt_rand_next) = state; } /* }}} */ @@ -145,11 +250,11 @@ static inline void php_mt_reload(void) PHPAPI void php_mt_srand(uint32_t seed) { /* Seed the generator with a simple uint32 */ - php_mt_initialize(seed, BG(state)); + php_mt_initialize(seed, RANDOM_G(mt_rand_state)); php_mt_reload(); /* Seed only once */ - BG(mt_rand_is_seeded) = 1; + RANDOM_G(mt_rand_is_seeded) = 1; } /* }}} */ @@ -161,7 +266,7 @@ PHPAPI uint32_t php_mt_rand(void) uint32_t s1; - if (UNEXPECTED(!BG(mt_rand_is_seeded))) { + if (UNEXPECTED(!RANDOM_G(mt_rand_is_seeded))) { zend_long bytes; if (php_random_bytes_silent(&bytes, sizeof(zend_long)) == FAILURE) { bytes = GENERATE_SEED(); @@ -169,12 +274,12 @@ PHPAPI uint32_t php_mt_rand(void) php_mt_srand(bytes); } - if (BG(left) == 0) { + if (RANDOM_G(mt_rand_left) == 0) { php_mt_reload(); } - --BG(left); + --RANDOM_G(mt_rand_left); - s1 = *BG(next)++; + s1 = *RANDOM_G(mt_rand_next)++; s1 ^= (s1 >> 11); s1 ^= (s1 << 7) & 0x9d2c5680U; s1 ^= (s1 << 15) & 0xefc60000U; @@ -182,6 +287,193 @@ PHPAPI uint32_t php_mt_rand(void) } /* }}} */ +/* {{{ php_random_bytes */ +PHPAPI zend_result 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_DECL_ARC4RANDOM_BUF && ((defined(__OpenBSD__) && OpenBSD >= 201405) || (defined(__NetBSD__) && __NetBSD_Version__ >= 700000001) || defined(__APPLE__)) + 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) + /* 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 zend_result 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()); +} +/* }}} */ + +/* {{{ 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)); +} +/* }}} */ + /* {{{ Seeds Mersenne Twister random number generator */ PHP_FUNCTION(mt_srand) { @@ -202,10 +494,10 @@ PHP_FUNCTION(mt_srand) switch (mode) { case MT_RAND_PHP: - BG(mt_rand_mode) = MT_RAND_PHP; + RANDOM_G(mt_rand_mode) = MT_RAND_PHP; break; default: - BG(mt_rand_mode) = MT_RAND_MT19937; + RANDOM_G(mt_rand_mode) = MT_RAND_MT19937; } php_mt_srand(seed); @@ -295,7 +587,7 @@ PHPAPI zend_long php_mt_rand_common(zend_long min, zend_long max) { int64_t n; - if (BG(mt_rand_mode) == MT_RAND_MT19937) { + if (RANDOM_G(mt_rand_mode) == MT_RAND_MT19937) { return php_mt_rand_range(min, max); } @@ -347,10 +639,113 @@ PHP_FUNCTION(mt_getrandmax) } /* }}} */ -PHP_MINIT_FUNCTION(mt_rand) +/* {{{ 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; + 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); +} +/* }}} */ + +/* {{{ PHP_MINIT_FUNCTION */ +PHP_MINIT_FUNCTION(random) +{ +#if defined(ZTS) && defined(COMPILE_DL_RANDOM) + ZEND_TSRMLS_CACHE_UPDATE(); +#endif + 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(lcg_seeded) = 0; + RANDOM_G(mt_rand_is_seeded) = 0; + RANDOM_G(random_fd) = -1; + return SUCCESS; } +/* }}} */ + +/* {{{ PHP_MSHUTDOWN_FUNCTION */ +PHP_MSHUTDOWN_FUNCTION(random) +{ +#if defined(ZTS) && defined(COMPILE_DL_RANDOM) + ZEND_TSRMLS_CACHE_UPDATE(); +#endif + + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_RSHUTDOWN_FUNCTION */ +PHP_RSHUTDOWN_FUNCTION(random) +{ + RANDOM_G(lcg_seeded) = 0; + RANDOM_G(mt_rand_is_seeded) = 0; + + return SUCCESS; +} +/* }}} */ + +/* {{{ random_module_entry */ +zend_module_entry random_module_entry = { + STANDARD_MODULE_HEADER, + "random", + ext_functions, + PHP_MINIT(random), + NULL, + NULL, + PHP_RSHUTDOWN(random), + NULL, + PHP_VERSION, + PHP_MODULE_GLOBALS(random), + NULL, + NULL, + NULL, + STANDARD_MODULE_PROPERTIES_EX +}; +/* }}} */ diff --git a/ext/random/random.stub.php b/ext/random/random.stub.php new file mode 100644 index 0000000000000..4b1dc99d3b5b4 --- /dev/null +++ b/ext/random/random.stub.php @@ -0,0 +1,28 @@ + | - +----------------------------------------------------------------------+ -*/ - -#include "php.h" -#include "php_lcg.h" - -#if 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/password.c b/ext/standard/password.c index 651cffc9fe656..69df37fe6347d 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" #if HAVE_ARGON2LIB #include "argon2.h" #endif diff --git a/ext/standard/php_lcg.h b/ext/standard/php_lcg.h index b18d422cf9de1..696a3efc2415f 100644 --- a/ext/standard/php_lcg.h +++ b/ext/standard/php_lcg.h @@ -1,38 +1,6 @@ -/* - +----------------------------------------------------------------------+ - | 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 +#include "ext/random/php_random.h" -#endif +#endif /* PHP_LCG_H */ diff --git a/ext/standard/php_mt_rand.h b/ext/standard/php_mt_rand.h index 169c2a23332b8..335d609340732 100644 --- a/ext/standard/php_mt_rand.h +++ b/ext/standard/php_mt_rand.h @@ -1,40 +1,6 @@ -/* - +----------------------------------------------------------------------+ - | 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); +#define PHP_MT_RAND_H -PHP_MINIT_FUNCTION(mt_rand); +#include "ext/random/php_random.h" #endif /* PHP_MT_RAND_H */ diff --git a/ext/standard/php_rand.h b/ext/standard/php_rand.h index e8018722772f7..fec98cb16a170 100644 --- a/ext/standard/php_rand.h +++ b/ext/standard/php_rand.h @@ -1,72 +1,6 @@ -/* - +----------------------------------------------------------------------+ - | 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); +#include "ext/random/php_random.h" #endif /* PHP_RAND_H */ diff --git a/ext/standard/php_random.h b/ext/standard/php_random.h index b4b492c6af6ab..d959d109037b8 100644 --- a/ext/standard/php_random.h +++ b/ext/standard/php_random.h @@ -1,50 +1,6 @@ -/* - +----------------------------------------------------------------------+ - | 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_LEGACY +#define PHP_RANDOM_H_LEGACY -#ifndef PHP_RANDOM_H -#define PHP_RANDOM_H +#include "ext/random/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 +#endif /* PHP_RANDOM_H_LEGACY */ 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/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 d6140ffd2ae25..0000000000000 --- a/ext/standard/random.c +++ /dev/null @@ -1,291 +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 __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_DECL_ARC4RANDOM_BUF && ((defined(__OpenBSD__) && OpenBSD >= 201405) || (defined(__NetBSD__) && __NetBSD_Version__ >= 700000001) || defined(__APPLE__)) - 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) { -#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(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 4608d954d4bd2..7fbfffa780569 100644 --- a/ext/standard/string.c +++ b/ext/standard/string.c @@ -18,7 +18,7 @@ #include #include "php.h" -#include "php_rand.h" +#include "ext/random/php_random.h" #include "php_string.h" #include "php_variables.h" #include diff --git a/ext/standard/uniqid.c b/ext/standard/uniqid.c index 04f81deeb2d91..285b0570a68f4 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..796edcdca73c4 100644 --- a/main/internal_functions_win32.c +++ b/main/internal_functions_win32.c @@ -36,7 +36,6 @@ #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" 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,