mirror of
https://github.com/php/php-src.git
synced 2025-08-15 13:38:49 +02:00
random: Move the CSPRNG implementation into a separate C file (#10668)
The CSPRNG is a delicate and security relevant piece of code and having it in the giant random.c makes it much harder to verify changes to it. Split it into a separate file.
This commit is contained in:
parent
d5c649b36b
commit
b14dd85dca
4 changed files with 254 additions and 213 deletions
|
@ -19,6 +19,7 @@ dnl Setup extension
|
||||||
dnl
|
dnl
|
||||||
PHP_NEW_EXTENSION(random,
|
PHP_NEW_EXTENSION(random,
|
||||||
random.c \
|
random.c \
|
||||||
|
csprng.c \
|
||||||
engine_combinedlcg.c \
|
engine_combinedlcg.c \
|
||||||
engine_mt19937.c \
|
engine_mt19937.c \
|
||||||
engine_pcgoneseq128xslrr64.c \
|
engine_pcgoneseq128xslrr64.c \
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
EXTENSION("random", "random.c", false /* never shared */, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
|
EXTENSION("random", "random.c", false /* never shared */, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
|
||||||
PHP_RANDOM="yes";
|
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 gammasection.c randomizer.c", "random");
|
ADD_SOURCES(configure_module_dirname, "csprng.c engine_combinedlcg.c engine_mt19937.c engine_pcgoneseq128xslrr64.c engine_xoshiro256starstar.c engine_secure.c engine_user.c gammasection.c randomizer.c", "random");
|
||||||
PHP_INSTALL_HEADERS("ext/random", "php_random.h");
|
PHP_INSTALL_HEADERS("ext/random", "php_random.h");
|
||||||
|
|
252
ext/random/csprng.c
Normal file
252
ext/random/csprng.c
Normal file
|
@ -0,0 +1,252 @@
|
||||||
|
/*
|
||||||
|
+----------------------------------------------------------------------+
|
||||||
|
| 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: Tim Düsterhus <timwolla@php.net> |
|
||||||
|
| Go Kudo <zeriyoshi@php.net> |
|
||||||
|
+----------------------------------------------------------------------+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
# include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include "php.h"
|
||||||
|
|
||||||
|
#include "Zend/zend_exceptions.h"
|
||||||
|
|
||||||
|
#include "php_random.h"
|
||||||
|
|
||||||
|
#if HAVE_UNISTD_H
|
||||||
|
# include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PHP_WIN32
|
||||||
|
# include "win32/time.h"
|
||||||
|
# include "win32/winutil.h"
|
||||||
|
# include <process.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
# include <sys/syscall.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if HAVE_SYS_PARAM_H
|
||||||
|
# include <sys/param.h>
|
||||||
|
# if (__FreeBSD__ && __FreeBSD_version > 1200000) || (__DragonFly__ && __DragonFly_version >= 500700) || \
|
||||||
|
defined(__sun) || (defined(__NetBSD__) && __NetBSD_Version__ >= 1000000000)
|
||||||
|
# include <sys/random.h>
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if HAVE_COMMONCRYPTO_COMMONRANDOM_H
|
||||||
|
# include <CommonCrypto/CommonCryptoError.h>
|
||||||
|
# include <CommonCrypto/CommonRandom.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __has_feature(memory_sanitizer)
|
||||||
|
# include <sanitizer/msan_interface.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
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(random_ce_Random_RandomException, "Failed to retrieve randomness from the operating system (BCryptGenRandom)", 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(random_ce_Random_RandomException, "Failed to retrieve randomness from the operating system (CCRandomGenerateBytes)", 0);
|
||||||
|
}
|
||||||
|
return FAILURE;
|
||||||
|
}
|
||||||
|
#elif HAVE_DECL_ARC4RANDOM_BUF && ((defined(__OpenBSD__) && OpenBSD >= 201405) || (defined(__NetBSD__) && __NetBSD_Version__ >= 700000001 && __NetBSD_Version__ < 1000000000) || \
|
||||||
|
defined(__APPLE__))
|
||||||
|
/*
|
||||||
|
* OpenBSD until there is a valid equivalent
|
||||||
|
* or NetBSD before the 10.x release
|
||||||
|
* falls back to arc4random_buf
|
||||||
|
* giving a decent output, the main benefit
|
||||||
|
* is being (relatively) failsafe.
|
||||||
|
* Older macOs releases fall also into this
|
||||||
|
* category for reasons explained above.
|
||||||
|
*/
|
||||||
|
arc4random_buf(bytes, size);
|
||||||
|
#else
|
||||||
|
size_t read_bytes = 0;
|
||||||
|
# if (defined(__linux__) && defined(SYS_getrandom)) || (defined(__FreeBSD__) && __FreeBSD_version >= 1200000) || (defined(__DragonFly__) && __DragonFly_version >= 500700) || \
|
||||||
|
defined(__sun) || (defined(__NetBSD__) && __NetBSD_Version__ >= 1000000000)
|
||||||
|
/* Linux getrandom(2) syscall or FreeBSD/DragonFlyBSD/NetBSD getrandom(2) function
|
||||||
|
* Being a syscall, implemented in the kernel, getrandom offers higher quality output
|
||||||
|
* compared to the arc4random api albeit a fallback to /dev/urandom is considered.
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
ssize_t n;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
# 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) {
|
||||||
|
errno = 0;
|
||||||
|
fd = open("/dev/urandom", O_RDONLY);
|
||||||
|
if (fd < 0) {
|
||||||
|
if (should_throw) {
|
||||||
|
if (errno != 0) {
|
||||||
|
zend_throw_exception_ex(random_ce_Random_RandomException, 0, "Cannot open /dev/urandom: %s", strerror(errno));
|
||||||
|
} else {
|
||||||
|
zend_throw_exception_ex(random_ce_Random_RandomException, 0, "Cannot open /dev/urandom");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
/* 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) {
|
||||||
|
if (errno != 0) {
|
||||||
|
zend_throw_exception_ex(random_ce_Random_RandomException, 0, "Error reading from /dev/urandom: %s", strerror(errno));
|
||||||
|
} else {
|
||||||
|
zend_throw_exception_ex(random_ce_Random_RandomException, 0, "Error reading from /dev/urandom");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FAILURE;
|
||||||
|
}
|
||||||
|
RANDOM_G(random_fd) = fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
read_bytes = 0;
|
||||||
|
while (read_bytes < size) {
|
||||||
|
errno = 0;
|
||||||
|
ssize_t n = read(fd, bytes + read_bytes, size - read_bytes);
|
||||||
|
|
||||||
|
if (n <= 0) {
|
||||||
|
if (should_throw) {
|
||||||
|
if (errno != 0) {
|
||||||
|
zend_throw_exception_ex(random_ce_Random_RandomException, 0, "Could not gather sufficient random data: %s", strerror(errno));
|
||||||
|
} else {
|
||||||
|
zend_throw_exception_ex(random_ce_Random_RandomException, 0, "Could not gather sufficient random data");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
read_bytes += (size_t) n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
|
@ -43,25 +43,8 @@
|
||||||
# include <sys/time.h>
|
# include <sys/time.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __linux__
|
|
||||||
# include <sys/syscall.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if HAVE_SYS_PARAM_H
|
#if HAVE_SYS_PARAM_H
|
||||||
# include <sys/param.h>
|
# include <sys/param.h>
|
||||||
# if (__FreeBSD__ && __FreeBSD_version > 1200000) || (__DragonFly__ && __DragonFly_version >= 500700) || \
|
|
||||||
defined(__sun) || (defined(__NetBSD__) && __NetBSD_Version__ >= 1000000000)
|
|
||||||
# include <sys/random.h>
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if HAVE_COMMONCRYPTO_COMMONRANDOM_H
|
|
||||||
# include <CommonCrypto/CommonCryptoError.h>
|
|
||||||
# include <CommonCrypto/CommonRandom.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if __has_feature(memory_sanitizer)
|
|
||||||
# include <sanitizer/msan_interface.h>
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "random_arginfo.h"
|
#include "random_arginfo.h"
|
||||||
|
@ -479,201 +462,6 @@ PHPAPI zend_long php_rand(void)
|
||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
||||||
/* {{{ 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(random_ce_Random_RandomException, "Failed to retrieve randomness from the operating system (BCryptGenRandom)", 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(random_ce_Random_RandomException, "Failed to retrieve randomness from the operating system (CCRandomGenerateBytes)", 0);
|
|
||||||
}
|
|
||||||
return FAILURE;
|
|
||||||
}
|
|
||||||
#elif HAVE_DECL_ARC4RANDOM_BUF && ((defined(__OpenBSD__) && OpenBSD >= 201405) || (defined(__NetBSD__) && __NetBSD_Version__ >= 700000001 && __NetBSD_Version__ < 1000000000) || \
|
|
||||||
defined(__APPLE__))
|
|
||||||
/*
|
|
||||||
* OpenBSD until there is a valid equivalent
|
|
||||||
* or NetBSD before the 10.x release
|
|
||||||
* falls back to arc4random_buf
|
|
||||||
* giving a decent output, the main benefit
|
|
||||||
* is being (relatively) failsafe.
|
|
||||||
* Older macOs releases fall also into this
|
|
||||||
* category for reasons explained above.
|
|
||||||
*/
|
|
||||||
arc4random_buf(bytes, size);
|
|
||||||
#else
|
|
||||||
size_t read_bytes = 0;
|
|
||||||
# if (defined(__linux__) && defined(SYS_getrandom)) || (defined(__FreeBSD__) && __FreeBSD_version >= 1200000) || (defined(__DragonFly__) && __DragonFly_version >= 500700) || \
|
|
||||||
defined(__sun) || (defined(__NetBSD__) && __NetBSD_Version__ >= 1000000000)
|
|
||||||
/* Linux getrandom(2) syscall or FreeBSD/DragonFlyBSD/NetBSD getrandom(2) function
|
|
||||||
* Being a syscall, implemented in the kernel, getrandom offers higher quality output
|
|
||||||
* compared to the arc4random api albeit a fallback to /dev/urandom is considered.
|
|
||||||
*/
|
|
||||||
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;
|
|
||||||
ssize_t n;
|
|
||||||
|
|
||||||
errno = 0;
|
|
||||||
# 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) {
|
|
||||||
errno = 0;
|
|
||||||
fd = open("/dev/urandom", O_RDONLY);
|
|
||||||
if (fd < 0) {
|
|
||||||
if (should_throw) {
|
|
||||||
if (errno != 0) {
|
|
||||||
zend_throw_exception_ex(random_ce_Random_RandomException, 0, "Cannot open /dev/urandom: %s", strerror(errno));
|
|
||||||
} else {
|
|
||||||
zend_throw_exception_ex(random_ce_Random_RandomException, 0, "Cannot open /dev/urandom");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
errno = 0;
|
|
||||||
/* 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) {
|
|
||||||
if (errno != 0) {
|
|
||||||
zend_throw_exception_ex(random_ce_Random_RandomException, 0, "Error reading from /dev/urandom: %s", strerror(errno));
|
|
||||||
} else {
|
|
||||||
zend_throw_exception_ex(random_ce_Random_RandomException, 0, "Error reading from /dev/urandom");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return FAILURE;
|
|
||||||
}
|
|
||||||
RANDOM_G(random_fd) = fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
read_bytes = 0;
|
|
||||||
while (read_bytes < size) {
|
|
||||||
errno = 0;
|
|
||||||
ssize_t n = read(fd, bytes + read_bytes, size - read_bytes);
|
|
||||||
|
|
||||||
if (n <= 0) {
|
|
||||||
if (should_throw) {
|
|
||||||
if (errno != 0) {
|
|
||||||
zend_throw_exception_ex(random_ce_Random_RandomException, 0, "Could not gather sufficient random data: %s", strerror(errno));
|
|
||||||
} else {
|
|
||||||
zend_throw_exception_ex(random_ce_Random_RandomException, 0, "Could not gather sufficient random data");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
read_bytes += (size_t) n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#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 */
|
/* {{{ Returns a value from the combined linear congruential generator */
|
||||||
PHP_FUNCTION(lcg_value)
|
PHP_FUNCTION(lcg_value)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue