Description
Random\Engine\Xoshiro256StarStar::__construct() rejects a seed that would leave the internal state all zero, because xoshiro256** with zero state produces 0 on every call forever:
if (UNEXPECTED(t[0] == 0 && t[1] == 0 && t[2] == 0 && t[3] == 0)) {
zend_argument_value_error(1, "must not consist entirely of NUL bytes");
RETURN_THROWS();
}
__unserialize() doesn't check the same invariant. If a caller feeds this payload through __unserialize(), the engine lands in the forbidden state, and everything downstream returns zero: generate(), Randomizer::getInt(), Randomizer::getBytes(), Randomizer::nextFloat(), and every other method that consumes the engine.
Reproduction
<?php
// Constructor guard works as intended:
try {
new Random\Engine\Xoshiro256StarStar(str_repeat("\0", 32));
} catch (\ValueError $e) {
echo $e->getMessage(), "\n";
// Random\Engine\Xoshiro256StarStar::__construct():
// Argument #1 ($seed) must not consist entirely of NUL bytes
}
// __unserialize() bypasses it:
$engine = new Random\Engine\Xoshiro256StarStar(42);
$engine->__unserialize([
[],
['0000000000000000', '0000000000000000', '0000000000000000', '0000000000000000'],
]);
$r = new Random\Randomizer($engine);
var_dump($r->getInt(1, 1_000_000)); // int(1)
var_dump(bin2hex($r->getBytes(16))); // "00000000000000000000000000000000"
var_dump($r->nextFloat()); // float(0)
Expected
__unserialize() should reject the all-zero state the same way __construct() does. Returning false from the unserialize callback in engine_xoshiro256starstar.c is enough; the Mt19937-aliased __unserialize() wrapper turns that into the standard "Invalid serialization data for ... object" exception.
Without the guard, a bad serialized payload (hand-crafted, or round-tripped from an attacker-controlled source) produces a Randomizer that silently returns 0 from every method.
PHP Version
master (PHP 8.6.0-dev). The unserialize callback has shipped without the zero-state check since Xoshiro256StarStar landed, so 8.3, 8.4, and 8.5 are affected too.
Description
Random\Engine\Xoshiro256StarStar::__construct()rejects a seed that would leave the internal state all zero, because xoshiro256** with zero state produces0on every call forever:__unserialize()doesn't check the same invariant. If a caller feeds this payload through__unserialize(), the engine lands in the forbidden state, and everything downstream returns zero:generate(),Randomizer::getInt(),Randomizer::getBytes(),Randomizer::nextFloat(), and every other method that consumes the engine.Reproduction
Expected
__unserialize()should reject the all-zero state the same way__construct()does. Returningfalsefrom theunserializecallback inengine_xoshiro256starstar.cis enough; the Mt19937-aliased__unserialize()wrapper turns that into the standard "Invalid serialization data for ... object" exception.Without the guard, a bad serialized payload (hand-crafted, or round-tripped from an attacker-controlled source) produces a Randomizer that silently returns 0 from every method.
PHP Version
master (PHP 8.6.0-dev). The
unserializecallback has shipped without the zero-state check since Xoshiro256StarStar landed, so 8.3, 8.4, and 8.5 are affected too.