root/regress/lib/libc/arc4random-fork/arc4random-fork.c
/*
 * Copyright (c) 2014 Google Inc.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/mman.h>
#include <sys/wait.h>
#include <assert.h>
#include <err.h>
#include <errno.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>

#define CHECK(x) assert(x)
#define CHECK_EQ(a, b) assert((a) == (b))
#define CHECK_NE(a, b) assert((a) != (b))
#define CHECK_GE(a, b) assert((a) >= (b))
#define CHECK_LE(a, b) assert((a) <= (b))

/* Test arc4random_buf(3) instead of arc4random(3). */
static int flagbuf;

/* Initialize arc4random(3) before forking. */
static int flagprefork;

enum {
        N = 4096
};

typedef struct {
        uint32_t x[N];
} Buf;

static int
isfullbuf(const Buf *buf)
{
        size_t i;
        for (i = 0; i < N; i++)
                if (buf->x[i])
                        return (1);
        return (0);
}

static void
fillbuf(Buf *buf)
{
        if (flagbuf) {
                arc4random_buf(buf->x, sizeof(buf->x));
        } else {
                size_t i;
                for (i = 0; i < N; i++)
                        buf->x[i] = arc4random();
        }
}

static void
usage()
{
        errx(1, "usage: arc4random-fork [-bp]");
}

static pid_t
safewaitpid(pid_t pid, int *status, int options)
{
        pid_t ret;
        do {
                ret = waitpid(pid, status, options);
        } while (ret == -1 && errno == EINTR);
        return (ret);
}

int
main(int argc, char *argv[])
{
        int opt, status;
        Buf *bufparent, *bufchildone, *bufchildtwo;
        pid_t pidone, pidtwo;
        size_t i, countone = 0, counttwo = 0, countkids = 0;

        /* Ensure SIGCHLD isn't set to SIG_IGN. */
        const struct sigaction sa = {
                .sa_handler = SIG_DFL,
        };
        CHECK_EQ(0, sigaction(SIGCHLD, &sa, NULL));

        while ((opt = getopt(argc, argv, "bp")) != -1) {
                switch (opt) {
                case 'b':
                        flagbuf = 1;
                        break;
                case 'p':
                        flagprefork = 1;
                        break;
                default:
                        usage();
                }
        }

        if (flagprefork)
                (void)arc4random();

        bufparent = mmap(NULL, sizeof(Buf), PROT_READ|PROT_WRITE,
            MAP_ANON|MAP_PRIVATE, -1, 0);
        CHECK_NE(MAP_FAILED, bufparent);

        bufchildone = mmap(NULL, sizeof(Buf), PROT_READ|PROT_WRITE,
            MAP_ANON|MAP_SHARED, -1, 0);
        CHECK_NE(MAP_FAILED, bufchildone);

        bufchildtwo = mmap(NULL, sizeof(Buf), PROT_READ|PROT_WRITE,
            MAP_ANON|MAP_SHARED, -1, 0);
        CHECK_NE(MAP_FAILED, bufchildtwo);

        pidone = fork();
        CHECK_GE(pidone, 0);
        if (pidone == 0) {
                fillbuf(bufchildone);
                _exit(0);
        }

        pidtwo = fork();
        CHECK_GE(pidtwo, 0);
        if (pidtwo == 0) {
                fillbuf(bufchildtwo);
                _exit(0);
        }

        fillbuf(bufparent);

        CHECK_EQ(pidone, safewaitpid(pidone, &status, 0));
        CHECK(WIFEXITED(status));
        CHECK_EQ(0, WEXITSTATUS(status));

        CHECK_EQ(pidtwo, safewaitpid(pidtwo, &status, 0));
        CHECK(WIFEXITED(status));
        CHECK_EQ(0, WEXITSTATUS(status));

        CHECK(isfullbuf(bufchildone));
        CHECK(isfullbuf(bufchildtwo));

        for (i = 0; i < N; i++) {
                countone += bufparent->x[i] == bufchildone->x[i];
                counttwo += bufparent->x[i] == bufchildtwo->x[i];
                countkids += bufchildone->x[i] == bufchildtwo->x[i];
        }

        /*
         * These checks are inherently probabilistic and theoretically risk
         * flaking, but there's less than a 1 in 2^40 chance of more than
         * one pairwise match between two vectors of 4096 32-bit integers.
         */
        CHECK_LE(countone, 1);
        CHECK_LE(counttwo, 1);
        CHECK_LE(countkids, 1);

        return (0);
}