Commit graph

52 commits

Author SHA1 Message Date
Tim Düsterhus
f34721cabd
random: Initialize the mode field when seeding in php_random_default_status() (#13690)
This is not just an issue due to missing initialization since moving the state
struct directly into the module globals. In earlier versions changing the mode
to `MT_RAND_PHP` within a single request would also affect the mode for
subsequent requests.

Original commit message follows:

This is a follow-up fix for GH-13579. The issue was detected in the nightly
MSAN build.

(cherry picked from commit bf0abd1629)
2024-03-13 20:13:48 +01:00
Tim Düsterhus
e059498c04
random: Fix unknown mt_srand() compatibility for unknown modes (#13544)
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.
2024-02-29 18:05:59 +01:00
Tim Düsterhus
97c6da1dec
random/standard: Correctly handle broken engines in php_array_pick_keys (#13138) 2024-01-14 13:01:29 +01:00
Niels Dossche
22040f5a8f Merge branch 'PHP-8.1' into PHP-8.2
* PHP-8.1:
  Fix undefined behaviour in GENERATE_SEED()
  Fix undefined behaviour when writing 32-bit values in phar/tar.c
2023-03-26 16:15:57 +02:00
Tim Düsterhus
5087931963
random: Add missing php.h include to php_random.h (#10764)
`PHPAPI` is defined in `php.h`. It appears that without the explicit include,
recent versions of Visual Studio Code’s intellisense (rightfully) no longer
detect `PHPAPI`. This will then lead to a misparsing of the file, because
`PHPAPI` is assumed to be the return type and the actual return type is assumed
to be the function name, thus expecting a semicolon after the actual return
type. This in turn colors the entire header in red due to the detected syntax
error(s), making development very hard / impossible.

This did not cause issues outside of the IDE use case, because apparently all
users of `php_random.h` include `php.h` before including `php_random.h`.
2023-03-06 17:33:52 +01:00
Máté Kocsis
7517cf3b97
Merge branch 'PHP-8.1' into PHP-8.2
- PHP-8.1:
  Fix GH-10292 1st param of mt_srand() has UNKNOWN default on PHP <8.3
2023-01-24 19:53:09 +01:00
Tim Düsterhus
57b362b7a9
random: Do not trust arc4random_buf() on glibc (#10390)
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.
2023-01-23 18:21:42 +01:00
Tim Düsterhus
32f503e4e3
random: Fix check before closing random_fd (#10247)
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.
2023-01-07 14:03:13 +01:00
Tim Düsterhus
350883db06
[ci skip] random: Trim trailing whitespace in randomizer.c
To keep the diff cleaner for future changes, such as #9664.
2022-11-27 19:10:45 +01:00
Tim Düsterhus
7f0b228f48
Fix pre-PHP 8.2 compatibility for php_mt_rand_range() with MT_RAND_PHP (#9839)
* 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
2022-10-28 16:52:43 +02:00
Tim Düsterhus
59a19d710b
Reduce scope of r in rand_rangeXX (#9678)
This variable is only accessed within a single iteration of the expansion loop.
2022-10-06 12:55:51 +02:00
Tim Düsterhus
1e9280e035
[ci skip] Trim trailing whitespace in php_random.h 2022-10-03 20:32:37 +02:00
Joshua Rüsweg
ca399841ad
Remove superfluous helper variable in Randomizer::getBytes() (#9563)
* Remove superfluous helper variable in Randomizer::getBytes()

* Reduce the scope of `result` in Randomizer::getBytes()

Co-authored-by: Tim Düsterhus <tim@bastelstu.be>
2022-09-20 17:41:31 +02:00
Remi Collet
28a4d7676a
declare random globals as public API 2022-09-20 13:20:23 +02:00
Bob Weinand
a01dd9feda Revert "Port all internally used classes to use default_object_handlers"
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.
2022-09-14 11:13:23 +02:00
Tim Düsterhus
ddf7a5d4d9
random: Validate that the arrays do not contain extra elements when unserializing (#9458)
* Apply `var_dump()` in 02_engine/all_serialize_error.phpt

This ensures that an undetected serialization error is clear identifiable in the output.

* random: Validate that the arrays do not contain extra elements when unserializing
2022-09-05 17:33:36 +02:00
David Bohman
f098193d15 GH-9464: Fix build on older macOs releases.
A simple check for CommonCrypto/CommonRandom.h does not work on earlier macOS.
Must also pull in sys/types.h for size_t, Availability.h for __OSX_AVAILABLE_STARTING,
and CommonCrypto/CommonCryptoError.h for CCCryptorStatus.

Closes GH-9479.
2022-09-04 02:12:28 +01:00
Tim Düsterhus
f7d426cca6
Unify structure for ext/random's randomizer tests (#9410)
* Unify structure for ext/random's engine tests (2)

This makes adjustments that were missed in
2d6a883b3a.

* Add `engines.inc` for ext/random tests

* Unify structure for ext/random's randomizer tests
2022-08-31 19:22:39 +02:00
Bob Weinand
94ee4f9834 Port all internally used classes to use default_object_handlers
Signed-off-by: Bob Weinand <bobwei9@hotmail.com>
2022-08-31 16:45:27 +02:00
Máté Kocsis
adb45a63c0
Fix GH-9186 @strict-properties can be bypassed using unserialization (#9354)
* Emit deprecation warnings when adding dynamic properties to classes during unserialization - this will become an Error in php 9.0.
  (Adding dynamic properties in other contexts was already a deprecation warning - the use case of unserialization was overlooked)
* Throw an error when attempting to add a dynamic property to a `readonly` class when unserializing
* Add new serialization methods `__serialize`/`__unserialize` for SplFixedArray to avoid creating deprecated dynamic
  properties that would then be added to the backing fixed-size array
* Don't add named dynamic/declared properties (e.g. $obj->foo) of SplFixedArray to the backing array when unserializing
* Update tests to declare properties or to expect the deprecation warning
* Add news entry

Co-authored-by: Tyson Andre <tysonandre775@hotmail.com>
2022-08-30 07:46:32 -04:00
Máté Kocsis
b4ec3e9bc0
Do not generate CONST_CS when registering constants (#9439) 2022-08-28 08:27:19 +02:00
Tim Düsterhus
cbb024cb3d
Select rand_rangeXX() variant only based on the requested range (#9418)
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
2022-08-25 09:42:32 +02:00
Tim Düsterhus
0f696e2934
Fix rand_range32() for umax = UINT32_MAX (#9416)
* 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
2022-08-24 14:25:51 +02:00
Tim Düsterhus
2d6a883b3a
Unify structure for ext/random's engine tests (#9321)
* Remove user_xoshiro128plusplus.phpt

This test does not exercise any of the code paths of the random extension and
thus is no value-add.

* Unify structure for ext/random's engine tests
2022-08-23 23:55:11 +02:00
David CARLIER
8e2d4e6b52
random left rotates annotating as const. (#9346) 2022-08-16 05:31:58 +01:00
Tim Düsterhus
3b48a2044d
Replace RuntimeException in Randomizer::nextInt() by RandomException (#9305)
* Replace RuntimeException in Randomizer::nextInt() by RandomException

* Add ext/random/tests/03_randomizer/nextint_error.phpt
2022-08-15 11:06:03 +02:00
Tim Düsterhus
1cd2d731ef
Handle all-zero state in Xoshiro256** (#9250)
- Retry if the CSPRNG generates a zero state.
- Throw ValueError if the user passes a zero state.

Fixes GH-9249
2022-08-05 14:38:57 +02:00
Tim Düsterhus
a191710a21
[ci skip] Run dos2unix on ext/random tests
One test inconsistently had CRLF newlines.
2022-08-04 21:01:04 +02:00
Tim Düsterhus
60ace13f9c
Fix undefined behavior of MT_RAND_PHP if range exceeds ZEND_LONG_MAX (#9197)
RAND_RANGE_BADSCALING() invokes undefined behavior when (max - min) >
ZEND_LONG_MAX, because the intermediate `double` might not fit into
`zend_long`.

Fix this by inlining a fixed version of the macro into Mt19937's range()
function. Fixing the macro itself cannot be done in the general case, because
the types of the inputs are not known. Instead of replacing one possibly broken
version with another possibly broken version, the macro is simply left as is
and should be removed in a future version.

The fix itself is simple: Instead of storing the "offset" in a `zend_long`, we
use a `zend_ulong` which is capable of storing the resulting double by
construction. With this fix the implementation of this broken scaling is
effectively identical to the implementation of php_random_range from a data
type perspective, making it easy to verify the correctness.

It was further empirically verified that the broken macro and the fix return
the same results for all possible values of `r` for several distinct pairs of
(min, max).

Fixes GH-9190
Fixes GH-9191
2022-08-03 18:46:36 +02:00
Tim Düsterhus
3331832b04
Add ext/random Exception hierarchy (#9220)
* 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
2022-08-02 20:04:28 +02:00
Tim Düsterhus
db84e44607
Fix PcgOneseq128XslRr64::__construct() definition in random.stub.php (#9235)
* Fix PcgOneseq128XslRr64::__construct() definition in random.stub.php

The second parameter does not actually exist for a Oneseq PCG. It was removed
from the RFC before it went into voting.

* [ci skip] Add PcgOneseq128XslRr64 stub fix to NEWS
2022-08-02 18:54:39 +02:00
Tim Düsterhus
a6922fdecd
Clean up the implementation of Randomizer::__construct() (#9222)
* Verify that the engine doesn't change in construct_twice.phpt

* Clean up the implementation of Randomizer::__construct()

Instead of manually checking whether the constructor was already called, we
rely on the `readonly` modifier of the `$engine` property.

Additionally use `object_init_ex()` instead of manually calling
`->create_object()`.
2022-08-02 17:30:18 +02:00
Tim Düsterhus
54e406cc50
Clean up nested exceptions without value-add in ext/random (#9211)
* Remove exception in Randomizer::shuffleBytes()

The only way that `php_binary_string_shuffle` fails is when the engine itself
fails. With the currently available list of engines we have:

- Mt19937            : Infallible.
- PcgOneseq128XslRr64: Infallible.
- Xoshiro256StarStar : Infallible.
- Secure             : Practically infallible on modern systems.
                       Exception messages were cleaned up in GH-9169.
- User               : Error when returning an empty string.
                       Error when seriously biased (range() fails).
                       And whatever Throwable the userland developer decides to use.

So the existing engines are either infallible or throw an Exception/Error with
a high quality message themselves, making this exception not a value-add and
possibly confusing.

* Remove exception in Randomizer::shuffleArray()

Same reasoning as in the previous commit applies.

* Remove exception in Randomizer::getInt()

Same reasoning as in the previous commit applies.

* Remove exception in Randomizer::nextInt()

Same reasoning as in the previous commit applies, except that it won't throw on
a seriously biased user engine, as `range()` is not used.

* Remove exception in Randomizer::getBytes()

Same reasoning as in the previous commit applies.

* Remove exception in Mt19937::generate()

This implementation is shared across all native engines. Thus the same
reasoning as the previous commits applies, except that the User engine does not
use this method. Thus is only applicable to the Secure engine, which is the
only fallible native engine.

* [ci skip] Add cleanup of Randomizer exceptions to NEWS
2022-08-02 17:29:36 +02:00
Tim Düsterhus
c63f18dd9b
Unify ext/random unserialize errors with ext/date (#9185)
* Unify ext/random unserialize errors with ext/date

- Use `Error` instead of `Exception`.
- Adjust wording.

* Make `zend_read_property` silent in `Randomizer::__unserialize()`

Having:

> Error: Typed property Random\Randomizer::$engine must not be accessed before
> initialization

is not a value-add in this case.

* Insert the actual class name in the unserialization error of Engines

* Revert unserialization failure back to Exception from Error

see https://news-web.php.net/php.internals/118311
2022-08-02 09:00:37 +02:00
Anton Smirnov
50bd8ba51c
PcgOneseq128XslRr64::jump(): Throw ValueError for negative $advance (#9213)
* PCG64: $advance must be non-negative

Closes GH-9212
2022-08-01 13:47:14 +01:00
zeriyoshi
4e92c74654
random: split Randomizer::getInt() without argument to Randomizer::nextInt()
Since argument overloading is not safe for reflection, the method needed
to be split appropriately.

Co-authored-by: Tim Düsterhus <timwolla@googlemail.com>

Closes GH-9057.
2022-08-01 12:19:22 +02:00
Tim Düsterhus
53ca24d46e
Improve phrasing in argument value errors in ext/random (#9206)
This rephrases the error message for argument errors to be a proper English
sentence.

Co-authored-by: Máté Kocsis <kocsismate@woohoolabs.com>
2022-07-31 19:27:28 +02:00
Tim Düsterhus
5aca25a134
[ci skip] Improve error message of an engine fails to seed from the CSPRNG (#9160) 2022-07-30 15:37:31 +02:00
Tim Düsterhus
b948f8048b
Improve error messages in php_random_bytes() (#9169) 2022-07-28 18:45:30 +02:00
Tim Düsterhus
d058acb4ac
Use ValueError if an invalid mode is passed to Mt19937 (#9159) 2022-07-27 09:03:02 +02:00
Tim Düsterhus
5c693c770a
Remove ->last_unsafe from php_random_status (#9132)
Whenever ->last_unsafe is set to `true` an exception has been thrown. Thus we
can replace the check for `->last_unsafe` with a check for `EG(exception)`
which is a much more natural way to ommunicate an error up the chain.
2022-07-26 09:02:51 +02:00
Tim Düsterhus
60f149f7ad
Improve error reporting in random extension (#9071)
* Use `php_random_bytes_throw()` in Secure engine's generate()

This exposes the underlying exception, improving debugging:

    Fatal error: Uncaught Exception: Cannot open source device in php-src/test.php:5
    Stack trace:
    #0 php-src/test.php(5): Random\Engine\Secure->generate()
    #1 {main}

    Next RuntimeException: Random number generation failed in php-src/test.php:5
    Stack trace:
    #0 php-src/test.php(5): Random\Engine\Secure->generate()
    #1 {main}
      thrown in php-src/test.php on line 5

* Use `php_random_int_throw()` in Secure engine's range()

This exposes the underlying exception, improving debugging:

    Exception: Cannot open source device in php-src/test.php:17
    Stack trace:
    #0 php-src/test.php(17): Random\Randomizer->getInt(1, 3)
    #1 {main}

    Next RuntimeException: Random number generation failed in php-src/test.php:17
    Stack trace:
    #0 php-src/test.php(17): Random\Randomizer->getInt(1, 3)
    #1 {main}

* Throw exception when a user engine returns an empty string

This improves debugging, because the actual reason for the failure is available
as a previous Exception:

    DomainException: The returned string must not be empty in php-src/test.php:17
    Stack trace:
    #0 php-src/test.php(17): Random\Randomizer->getBytes(123)
    #1 {main}

    Next RuntimeException: Random number generation failed in php-src/test.php:17
    Stack trace:
    #0 php-src/test.php(17): Random\Randomizer->getBytes(123)
    #1 {main}

* Throw exception when the range selector fails to get acceptable numbers in 50 attempts

This improves debugging, because the actual reason for the failure is available
as a previous Exception:

    RuntimeException: Failed to generate an acceptable random number in 50 attempts in php-src/test.php:17
    Stack trace:
    #0 php-src/test.php(17): Random\Randomizer->getInt(1, 3)
    #1 {main}

    Next RuntimeException: Random number generation failed in php-src/test.php:17
    Stack trace:
    #0 php-src/test.php(17): Random\Randomizer->getInt(1, 3)
    #1 {main}

* Improve user_unsafe test

Select parameters for ->getInt() that will actually lead to unsafe behavior.

* Fix user_unsafe test

If an engine fails once it will be permanently poisoned by setting
`->last_unsafe`. This is undesirable for the test, because it skews the
results.

Fix this by creating a fresh engine for each "assertion".

* Remove duplication in user_unsafe.phpt

* Catch `Throwable` in user_unsafe.phpt

As we print the full stringified exception we implicitly assert the type of the
exception. No need to be overly specific in the catch block.

* Throw an error if an engine returns an empty string

* Throw an Error if range fails to find an acceptable number in 50 attempts
2022-07-25 09:00:49 +02:00
Go Kudo
34b352d121
Fix memory leak on Randomizer::__construct() call twice (#9091)
When Radomizer::__construct() was called with no arguments, Randomizer\Engine\Secure was implicitly instantiate and memory was leaking.
Co-authored-by: Tim Düsterhus <timwolla@googlemail.com>
2022-07-24 03:09:14 +09:00
Tim Düsterhus
395b6a9674
Remove dead code in ext/random/random.c (#9114)
see GH-9070
2022-07-23 13:14:31 +02:00
Máté Kocsis
98be397776
Declare ext/random constants in stubs (#9109) 2022-07-23 12:32:01 +02:00
Tim Düsterhus
ab5491f505
Fix shift in rand_rangeXX() (#9088)
The previous shifting logic is problematic for two reasons:

1. It invokes undefined behavior when the `->last_generated_size` is at least
as large as the target integer in `result`, because the shift is larger than
the target integer. This was reported in GH-9083.

2. It expands the returned bytes in a big-endian fashion: Earlier bytes are
shifting into the most-significant position. As all the other logic in the
random extension treats byte-strings as little-endian numbers this is
inconsistent.

By fixing the second issue, we can implicitly fix the first one: Instead of
shifting the existing bits by the number of "newly added" bits, we shift the
newly added bits by the number of existing bits. As we stop requesting new bits
once the total_size reached the size of the target integer we can be sure to
never invoke undefined behavior during shifting.

The get_int_user.phpt test was adjusted to verify the little-endian behavior.
It generates a single byte per call and we expect the first byte generated to
appear at the start of the resulting number.

see GH-9056 for a previous fix in the same area.
Fixes GH-9083 which reports the undefined behavior.
Resolves GH-9085 which was an alternative attempt to fix GH-9083.
2022-07-22 10:45:17 +01:00
Go Kudo
133b9b08da
Avoid signed integer overflow in php_random_range() (#9066) 2022-07-22 10:57:32 +09:00
Go Kudo
e4c894984f
[ci skip] Update EXTENSIONS and Author(s) in ext/random (#9074)
php.net account is better suited for this cases.
2022-07-21 17:53:32 +02:00
Christoph M. Becker
8487d8fa91
Fix GH-9067: random extension is not thread safe
For thread-safety, we need to initialize global variables in GINIT (or
RINIT), but not in MINIT.

Closes GH-9070.
2022-07-21 12:53:07 +02:00
Tim Düsterhus
804c3fc821
Fix byte expansion in rand_rangeXX() (#9056)
* Fix shift in rand_range??()

The last generated size is in bytes, whereas the shift is in bits. Multiple the
generated size by 8 to correctly handle each byte once.

* Correctly handle user engines returning less than 4 bytes in rand_rangeXX()

We need to loop until we accumulate sufficient bytes, instead of just checking
once. The version in the rejection loop was already correct.

* Clean up some repetition in rand_rangeXX()
2022-07-20 17:33:10 +02:00