Subject: lib/25367: arc4random state is shared across forks
To: None <gnats-bugs@gnats.NetBSD.org>
From: None <apb@cequrux.com>
List: netbsd-bugs
Date: 04/28/2004 17:31:01
>Number:         25367
>Category:       lib
>Synopsis:       arc4random state is shared across forks
>Confidential:   no
>Severity:       serious
>Priority:       high
>Responsible:    lib-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Wed Apr 28 15:32:00 UTC 2004
>Closed-Date:
>Last-Modified:
>Originator:     Alan Barrett
>Release:        NetBSD 1.6ZK
>Organization:
	Not much
>Environment:
System: NetBSD 1.6ZK
Architecture: i386
Machine: i386
>Description:
arc4random() returns the same stream of values in all children of
a common parent process.  This violates the principle of least
astonishment.

>How-To-Repeat:
sh$ ./test_arc4random
top-level process 28045: toprand=0xb70ed1c5
child process 22848: childrand[0]=0x617d6e3e
child process 22848: childrand[1]=0xd9af1d32
child process 14579: childrand[0]=0x617d6e3e
child process 14579: childrand[1]=0xd9af1d32
child process 5365: childrand[0]=0x617d6e3e
child process 5365: childrand[1]=0xd9af1d32
child process 17748: childrand[0]=0x617d6e3e
child process 17748: childrand[1]=0xd9af1d32
child process 17560: childrand[0]=0x617d6e3e
child process 17560: childrand[1]=0xd9af1d32
sh$ cat ./test_arc4random.c
#include <unistd.h>
#include <stdlib.h>
#include <inttypes.h>
#include <sys/wait.h>

#define CHILDREN 5		/* number of children to run */
#define RAND_PER_CHILD 2	/* number of arc4random() calls per child */

int
main(int argc, char **argv)
{
    int nchildren = 0;
    int nerrors = 0;
    pid_t toppid = getpid();
    u_int32_t toprand;

    /* initialise arc4random()'s private data in the parent */
    toprand = arc4random();
    printf("top-level process %lu: toprand=0x%" PRIx32 "\n",
		(unsigned long)toppid, toprand);
    fflush(stdout);

    for (nerrors = nchildren = 0; nchildren < CHILDREN && nerrors <= 10; ) {
	pid_t forkresult = fork();
	if (forkresult < 0) {
	    nerrors++;
	    sleep(1);
	} else if (forkresult == 0) {
	    pid_t childpid = getpid();
	    u_int32_t childrand;
	    int i;
	    for (i = 0; i < RAND_PER_CHILD; i++) {
		childrand = arc4random();
		printf("child process %lu: childrand[%d]=0x%" PRIx32 "\n",
		    (unsigned long)childpid, i, childrand);
	    }
	    fflush(stdout);
	    exit(0);
	} else {
	    nchildren++;
	    (void) wait(NULL);
	}
    }
    exit(0);
}

>Fix:
Somehow make arc4random re-seed itself after a fork().

Since there seems to be no fork_hooks infrastructure, I suppose we could
save the pid somewhere and re-seed if getpid() says the pid has changed.

>Release-Note:
>Audit-Trail:
>Unformatted: