Skip to content

pcntl_fork() should reseed the MT rand in all children #21351

@anthonyryan1

Description

@anthonyryan1

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions