-
Notifications
You must be signed in to change notification settings - Fork 8k
Description
Description
We recently discovered that some of our code, was producing identical random numbers every single run.
We ended up tracing it to a 3rd party library from packagist which started using MT rand when it hadn't before.
Prior to that update, the first use of MT rand occurred after the pcntl_fork(), so we got a unique MT seed in each child process.
After the update, despite no changes near this code, we suddenly saw the exact same output from array_rand() every single time. A CSPRNG would not be affected by this.
I've mitigated this in my codebase, by ensuring we always call mt_srand() in the child immediately after every pcntl_fork() call. But the fact that it could work for years then suddenly change because the MT rand was initialized prior to the fork was surprising behavior.
I would like to propose that pcntl_fork() should automatically reseed mt_rand() (if it has already been seeded) to help ensure consistent behavior regardless of whether MT rand was used prior to forking or not.
<?php
declare(strict_types=1);
// MT rand is used under the hood by a number of functions like array_rand()
// if it's not used prior to pcntl_fork() (eg. by commenting the next line) then the behavior changes
mt_rand();
for ($i = 0; $i < 10; $i++) {
$PID = pcntl_fork();
if ($PID == 0) {
// It's important to always reiniatilize the MT seed after forking
// MT rand seed is lazy initialized and that leads to inconsistent behavior:
// a. If MT rand is first used after the fork, it works correctly
// b. If MT rand is first used before the fork, then the child will inherit the seed
// and produce the exact same "random" number sequence every single run
// We learned this after it worked for years, then an innocuous change in a composer
// library call prior to the fork, suddenly caused the same predictable sequence every time
// mt_srand()
echo "child $i: " . array_rand(["a","b","c","d","e","f","g","h"]) . "\n";
exit;
} else {
pcntl_wait($Status);
}
}Resulted in this output:
child 0: 7
child 1: 7
child 2: 7
child 3: 7
child 4: 7
child 5: 7
child 6: 7
child 7: 7
child 8: 7
child 9: 7
But I expected this output instead:
child 0: 4
child 1: 7
child 2: 1
child 3: 4
child 4: 3
child 5: 3
child 6: 0
child 7: 3
child 8: 7
child 9: 1
PHP Version
PHP 8.5.3 (cli) (built: Mar 5 2026 20:29:09) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.5.3, Copyright (c) Zend Technologies
with Zend OPcache v8.5.3, Copyright (c), by Zend Technologies
Operating System
Gentoo Linux