/* Test that subprocesses generate distinct streams of randomness. Copyright (C) 2022-2024 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, see . */ /* Collect random data from subprocesses and check that all the results are unique. */ #include #include #include #include #include #include #include #include #include /* Perform multiple runs. The subsequent runs start with an already-initialized random number generator. */ enum { runs = 10 }; /* Total number of spawned processes on each run. */ enum { subprocesses = 49 }; /* The total number of processes. */ enum { processes = subprocesses + 1 }; /* Number of bytes of randomness to generate per process. Large enough to make false positive duplicates extremely unlikely. */ enum { random_size = 16 }; /* Generated bytes of randomness. */ struct result { unsigned char bytes[random_size]; }; /* Shared across all processes. */ static struct shared_data { pthread_barrier_t barrier; struct result results[runs][processes]; } *shared_data; static void generate_arc4random (unsigned char *bytes) { for (int i = 0; i < random_size / sizeof (uint32_t); i++) { uint32_t x = arc4random (); memcpy (&bytes[4 * i], &x, sizeof x); } } static void generate_arc4random_buf (unsigned char *bytes) { arc4random_buf (bytes, random_size); } static void generate_arc4random_uniform (unsigned char *bytes) { for (int i = 0; i < random_size; i++) bytes[i] = arc4random_uniform (256); } /* Invoked to collect data from a subprocess. */ static void subprocess (int run, int process_index, void (*func)(unsigned char *)) { xpthread_barrier_wait (&shared_data->barrier); func (shared_data->results[run][process_index].bytes); } /* Used to sort the results. */ struct index { int run; int process_index; }; /* Used to sort an array of struct index values. */ static int index_compare (const void *left1, const void *right1) { const struct index *left = left1; const struct index *right = right1; return memcmp (shared_data->results[left->run][left->process_index].bytes, shared_data->results[right->run][right->process_index].bytes, random_size); } static int do_test_func (void (*func)(unsigned char *bytes)) { /* Collect random data. */ for (int run = 0; run < runs; ++run) { pid_t pids[subprocesses]; for (int process_index = 0; process_index < subprocesses; ++process_index) { pids[process_index] = xfork (); if (pids[process_index] == 0) { subprocess (run, process_index, func); _exit (0); } } /* Trigger all subprocesses. Also add data from the parent process. */ subprocess (run, subprocesses, func); for (int process_index = 0; process_index < subprocesses; ++process_index) { int status; xwaitpid (pids[process_index], &status, 0); if (status != 0) FAIL_EXIT1 ("subprocess index %d (PID %d) exit status %d\n", process_index, (int) pids[process_index], status); } } /* Check for duplicates. */ struct index indexes[runs * processes]; for (int run = 0; run < runs; ++run) for (int process_index = 0; process_index < processes; ++process_index) indexes[run * processes + process_index] = (struct index) { .run = run, .process_index = process_index }; qsort (indexes, array_length (indexes), sizeof (indexes[0]), index_compare); for (size_t i = 1; i < array_length (indexes); ++i) { if (index_compare (indexes + i - 1, indexes + i) == 0) { support_record_failure (); unsigned char *bytes = shared_data->results[indexes[i].run] [indexes[i].process_index].bytes; char *quoted = support_quote_blob (bytes, random_size); printf ("error: duplicate randomness data: \"%s\"\n" " run %d, subprocess %d\n" " run %d, subprocess %d\n", quoted, indexes[i - 1].run, indexes[i - 1].process_index, indexes[i].run, indexes[i].process_index); free (quoted); } } return 0; } static int do_test (void) { shared_data = support_shared_allocate (sizeof (*shared_data)); { pthread_barrierattr_t attr; xpthread_barrierattr_init (&attr); xpthread_barrierattr_setpshared (&attr, PTHREAD_PROCESS_SHARED); xpthread_barrier_init (&shared_data->barrier, &attr, processes); xpthread_barrierattr_destroy (&attr); } do_test_func (generate_arc4random); do_test_func (generate_arc4random_buf); do_test_func (generate_arc4random_uniform); xpthread_barrier_destroy (&shared_data->barrier); support_shared_free (shared_data); shared_data = NULL; return 0; } #define TIMEOUT 40 #include