Skip to content

ReseedingRng fork handling is ~~broken~~ not as expected #1362

@thomcc

Description

@thomcc

The ReseedingRng claims to protect against fork:

// Fork protection
//
// We implement fork protection on Unix using `pthread_atfork`.
// When the process is forked, we increment `RESEEDING_RNG_FORK_COUNTER`.
// Every `ReseedingRng` stores the last known value of the static in
// `fork_counter`. If the cached `fork_counter` is less than
// `RESEEDING_RNG_FORK_COUNTER`, it is time to reseed this RNG.
//
// If reseeding fails, we don't deal with this by setting a delay, but just
// don't update `fork_counter`, so a reseed is attempted as soon as
// possible.
, however this doesn't seem to work (at least the way it's used in the thread_rng).

Here's an example that shows that after fork, the parent and all child processes will still have the same state for the thread rng: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=f63e9a09df0ec7f67d84dacb6d39ac32, which produces output like:

parent (pid 11): random::<u64>(): 0x2fe7d602f7aba388
parent (pid 11): spawned child 0 (pid 27)
parent (pid 11): spawned child 1 (pid 28)
parent (pid 11): spawned child 2 (pid 29)
parent (pid 11): spawned child 3 (pid 30)
child 3 (pid 30): random::<u64>(): 0x2907c1d06b61f595
parent (pid 11): spawned child 4 (pid 31)
parent (pid 11): finished spawning 5 child procs
child 4 (pid 31): random::<u64>(): 0x2907c1d06b61f595
parent (pid 11): random::<u64>(): 0x2907c1d06b61f595
child 2 (pid 29): random::<u64>(): 0x2907c1d06b61f595
child 1 (pid 28): random::<u64>(): 0x2907c1d06b61f595
child 0 (pid 27): random::<u64>(): 0x2907c1d06b61f595

I haven't dug deeply into why what you do currently is wrong, but it causes very tough to track-down bugs. CC @joshlf, who filed #1169, leading to the current design of the fork handling.

Background / why I'm stuck with fork: I have code that runs as a Postgres extension (https://github.com/pgcentralfoundation/pgrx), and PG will will run some init code in the parent, but most code runs in a child process (forked per-connection). Both used the uuid crate, which (apparently -- some dep in our tree seems to have turned it on) has a fastrand feature for using the rand crate. At the moment we've avoided use of uuids in that code (it didn't actually need them), but if rand is going to try to handle this at all, it might as well be right.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions