* Replace WIN32 conditions with _WIN32 or PHP_WIN32
WIN32 is defined by the SDK and not defined all the time on Windows by
compilers or the environment. _WIN32 is defined as 1 when the
compilation target is 32-bit ARM, 64-bit ARM, x86, or x64. Otherwise,
undefined.
This syncs these usages one step further.
Upstream libgd has replaced WIN32 with _WIN32 via
c60d9fe577
PHP_WIN32 is added to ext/sockets/sockets.stub.php as done in other
*.stub.php files at this point.
* Use PHP_WIN32 in ext/random
* Use PHP_WIN32 in ext/sockets
* Use _WIN32 in xxhash.h as done upstream
See https://github.com/Cyan4973/xxHash/pull/931
* Update end comment with PHP_WIN32
* random: Remove `php_rand()`
This effectively is just a slim wrapper around `(zend_long)php_mt_rand()`. It
is not compatible between 32-bit and 64-bit builds of PHP, due to the use of
`zend_long`, which may result in negative integersbeing returned on 32-bit
platforms, whereas 64-bit platforms will be compatible with `php_mt_rand()`. An
example would be the `0` seed, which emits 2357136044 on 64-bit platforms and
-1937831252 on 32-bit platforms.
Users of `php_rand()` should ideally migrate to one of the more modern engines,
with extension-specific state. If drop-in compatibility is desired, they can
just cast the result of `php_mt_rand()`. But providing it out of the box does
not provide a value-add and is potentially dangerous.
* random: Remove `php_srand()`
With `php_rand()` gone, preserving its companion `php_srand()` is just
confusing. The same recommendations apply: Migrate to a modern engine if
possible and just call `php_mt_srand()` with an appropriately casted input.
* random: Remove `PHP_RAND_MAX` and `RAND_MAX`
These are the companions to `php_rand()`, which was removed in a previous
commit.
Generally speaking the maximum returnable value is not particularly useful
anyways. Attempting it to create a random float by dividing the returned
integer by the maximum value would result in a bias if the maximum value would
be larger than 2**53 and even for that case, the various `range()` helpers
allow to easily retrieve a uniformly distributed integer from a suitable range.
* UPGRADING.INTERNALS
As all the input bits and pieces are mixed with SHA-1, cross-architecture
compatibility is not required and we can just mix in whatever they may look
like in memory, instead of going through the `write_*()` helpers that were
created for a previous in-development version that first filled a buffer that
was then hashed (allowing for easy inspection of the input data, but making it
harder to safely add values without checking for buffer overflows all the
time).
This change should also fix a build error on macOS ZTS: The thread ID is an
opaque type and not guaranteed to be arithmetic as per IEEE Std 1003.1-2017.
And indeed macOS defines it as a pointer to a structure, failing due to the
implicit pointer to integer conversion.
These are always dynamically allocated in GINIT, thus always take up memory. By
embedding them here we can avoid the dynamic allocation and additional pointer
indirection accessing them.
The test script:
<?php
for ($i = 0; $i < 9999999; $i++) mt_rand(1, 100);
Appears to run slightly faster with this change applied: Before this change it
always ran in just over 3 seconds, after this change I was also seeing times
below 3 seconds. Howver results are too close and too jittery to state this
performance improvement as a fact.
PHP 8.1 and below interpreted unknown modes as `MT_RAND_MT19937`, but PHP 8.2+
interprets them as `MT_RAND_PHP`.
Align the behavior with PHP 8.1 and below, because folks should be steered
towards the standard mode.
* random: Expose xoshiro256**'s seeding functions
* random: Expose pcgoneseq128xslrr64's seeding functions
* random: Expose Mt19937's seeding functions
* random: Expose CombinedLCG's seeding functions
* random: Call php_random_mt19937_seed32 to seed the global Mt19937
This avoids the function pointer indirection and improves type safety.
* random: NULL the generic seeding function
Different engines work quite differently, it is not useful to attempt to seed
them in a generic way using a 64 bit integer. As an example Mt19937 completely
ignores the upper 32 bits.
* random: Remove the `seed` member from `php_random_algo`
See the explanation in the previous commit for the reasoning. This member is
unused since the previous commit and was not consistently available even before
that (specifically for the Secure engine).
* UPGRADING.INTERNALS
* random: Remove useless cast in `php_mt_srand()`
* random: Remove `php_random_status`
Since 162e1dce98, the `php_random_status` struct
contains just a single `void*`, resulting in needless indirection when
accessing the engine state and thus decreasing readability because of the
additional non-meaningful `->state` references / the local helper variables.
There is also a small, but measurable performance benefit:
<?php
$e = new Random\Engine\Xoshiro256StarStar(0);
$r = new Random\Randomizer($e);
for ($i = 0; $i < 15; $i++)
var_dump(strlen($r->getBytes(100000000)));
goes from roughly 3.85s down to 3.60s.
The names of the `status` variables have not yet been touched to keep the diff
small. They will be renamed to the more appropriate `state` in a follow-up
cleanup commit.
* Introduce `php_random_algo_with_state`
This allows consumers of just the CSPRNG to include a much smaller header. It
also allows to verify at a glance whether a source file might use non-secure
randomness.
This commit includes the new header wherever the CSPRNG is used, possibly
replacing the inclusion of php_random.h if nothing else is used, but also
includes it in the main php_random.h header for compatibility.
Somewhat related to 45f8cfaf10,
2b30f18708, and
b14dd85dca.
Instead of returning the generated `uint64_t` and providing the size (i.e. the
number of bytes of the generated value) out-of-band via the
`last_generated_size` member of the `php_random_status` struct, the `generate`
function is now expected to return a new `php_random_result` struct containing
both the `size` and the `result`.
This has two benefits, one for the developer:
It's no longer possible to forget setting `last_generated_size` to the correct
value, because it now happens at the time of returning from the function.
and the other benefit is for performance:
The `php_random_result` struct will be returned as a register pair, thus the
`size` will be directly available without reloading it from main memory.
Checking a simplified version of `php_random_range64()` on Compiler Explorer
(“Godbolt”) with clang 17 shows a single change in the resulting assembly
showcasing the improvement (https://godbolt.org/z/G4WjdYxqx):
- add rbp, qword ptr [r14]
+ add rbp, rdx
Empirical testing confirms a measurable performance increase for the
`Randomizer::getBytes()` method:
<?php
$e = new Random\Engine\Xoshiro256StarStar(0);
$r = new Random\Randomizer($e);
var_dump(strlen($r->getBytes(100000000)));
goes from 250ms (before the change) to 220ms (after the change). While
generating 100 MB of random data certainly is not the most common use case, it
confirms the theoretical improvement in practice.
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.
* random: Convert the urandom loop into a while() loop
This allows us to more easily reduce the scope of `n` in a future commit and
now matches the getrandom(2) loop.
* random: Move the errno reset immediately above the getrandom(2) call
* random: Reduce the scope of `n` in the CSPRNG
* random: Declare `n` outside of preprocessor branch
The only way the previous `if (read_bytes < size)` branch could be taken is
when the loop was exited by the `break;` statement. We can just merge this into
the loop to make the code more obvious.
This effectively reverts #8984.
As discussed in #10327 which will enable the use of the getrandom(2) syscall on
NetBSD instead of relying on the userland arc4random_buf(), the CSPRNG should
prioritize security over speed [1] and history has shown that userland
implementations unavoidably fall short on the security side. In fact the glibc
implementation is a thin wrapper around the syscall due to security concerns
and thus does not provide any benefit over just calling getrandom(2) ourselves.
Even without any performance optimizations the CSPRNG should be plenty fast for
the vast majority of applications, because they often only need a few bytes of
randomness to generate a session ID. If speed is desired, the OO API offers
faster, but non-cryptographically secure engines.
It cannot be decided whether the device is available at build time, PHP might
run in a container or chroot that does not expose the device. Simply attempt to
open it, if it does not exist it will fail.
This improves readability of php_random_bytes() by removing one layer of
preprocessor conditions.
If, for whatever reason, the random_fd has been assigned file descriptor `0` it
previously failed to close during module shutdown, thus leaking the descriptor.
* random: Add Randomizer::nextFloat()
* random: Check that doubles are IEEE-754 in Randomizer::nextFloat()
* random: Add Randomizer::nextFloat() tests
* random: Add Randomizer::getFloat() implementing the y-section algorithm
The algorithm is published in:
Drawing Random Floating-Point Numbers from an Interval. Frédéric
Goualard, ACM Trans. Model. Comput. Simul., 32:3, 2022.
https://doi.org/10.1145/3503512
* random: Implement getFloat_gamma() optimization
see https://github.com/php/php-src/pull/9679/files#r994668327
* random: Add Random\IntervalBoundary
* random: Split the implementation of γ-section into its own file
* random: Add tests for Randomizer::getFloat()
* random: Fix γ-section for 32-bit systems
* random: Replace check for __STDC_IEC_559__ by compile-time check for DBL_MANT_DIG
* random: Drop nextFloat_spacing.phpt
* random: Optimize Randomizer::getFloat() implementation
* random: Reject non-finite parameters in Randomizer::getFloat()
* random: Add NEWS/UPGRADING for Randomizer’s float functionality
* Fix pre-PHP 8.2 compatibility for php_mt_rand_range() with MT_RAND_PHP
As some left-over comments indicated:
> Legacy mode deliberately not inside php_mt_rand_range()
> to prevent other functions being affected
The broken scaler was only used for `php_mt_rand_common()`, not
`php_mt_rand_range()`. The former is only used for `mt_rand()`, whereas the
latter is used for `array_rand()` and others.
With the refactoring for the introduction of ext/random `php_mt_rand_common()`
and `php_mt_rand_range()` were accidentally unified, thus introducing a
behavioral change that was reported in FakerPHP/Faker#528.
This commit moves the checks for `MT_RAND_PHP` from the general-purpose
`range()` function back into `php_mt_rand_common()` and also into
`Randomizer::getInt()` for drop-in compatibility with `mt_rand()`.
* [ci skip] NEWS for `MT_RAND_PHP` compatibility
This reverts commit 94ee4f9834.
The commit was a bit too late to be included in PHP 8.2 RC1. Given it's a massive ABI break, we decide to postpone the change to PHP 8.3.
This fixes an incompatibility when wrapping native 32-bit engines with a userland
engine. The latter always used the 64-bit range function which then used two
32-bit numbers from the underlying engine to fill the 64-bit range, whereas the
native implementation used only one.
Now the selection of the range variant only depends on the requested range. A
32-bit range uses the 32-bit variant (even for 64-bit engines), whereas a
larger range uses the 64-bit variant.
This was found in https://github.com/php/php-src/pull/9410#discussion_r953213000
* Fix rand_range32() for umax = UINT32_MAX
This was introduced in the commit that added the random extension:
4d8dd8d258.
Resolves GH-9415
* [ci skip] Rename `$r` to `$randomizer` in gh9415.phpt
* Make gh9415.phpt deterministic
* Make gh9415.phpt compatible with 32-bit
* Add Random\Random{Error,Exception} and Random\BrokenRandomEngineError
* Throw BrokenRandomEngineError
* Throw RandomException on seeding failure
* Throw RandomException when CSPRNG fails
* Remove unused include from ext/random/engine_combinedlcg.c
* Remove unused include from ext/random/engine_secure.c
* Remove unused include from ext/random/random.c
* [ci skip] Add ext/random Exception hierarchy to NEWS
* [ci skip] Add the change of Exception for random_(int|bytes) to UPGRADING