1/* Test that subprocesses generate distinct streams of randomness.
2 Copyright (C) 2022-2024 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19/* Collect random data from subprocesses and check that all the
20 results are unique. */
21
22#include <array_length.h>
23#include <stdlib.h>
24#include <stdio.h>
25#include <string.h>
26#include <support/check.h>
27#include <support/support.h>
28#include <support/xthread.h>
29#include <support/xunistd.h>
30#include <unistd.h>
31
32/* Perform multiple runs. The subsequent runs start with an
33 already-initialized random number generator. */
34enum { runs = 10 };
35
36/* Total number of spawned processes on each run. */
37enum { subprocesses = 49 };
38
39/* The total number of processes. */
40enum { processes = subprocesses + 1 };
41
42/* Number of bytes of randomness to generate per process. Large
43 enough to make false positive duplicates extremely unlikely. */
44enum { random_size = 16 };
45
46/* Generated bytes of randomness. */
47struct result
48{
49 unsigned char bytes[random_size];
50};
51
52/* Shared across all processes. */
53static struct shared_data
54{
55 pthread_barrier_t barrier;
56 struct result results[runs][processes];
57} *shared_data;
58
59static void
60generate_arc4random (unsigned char *bytes)
61{
62 for (int i = 0; i < random_size / sizeof (uint32_t); i++)
63 {
64 uint32_t x = arc4random ();
65 memcpy (&bytes[4 * i], &x, sizeof x);
66 }
67}
68
69static void
70generate_arc4random_buf (unsigned char *bytes)
71{
72 arc4random_buf (buf: bytes, size: random_size);
73}
74
75static void
76generate_arc4random_uniform (unsigned char *bytes)
77{
78 for (int i = 0; i < random_size; i++)
79 bytes[i] = arc4random_uniform (upper_bound: 256);
80}
81
82/* Invoked to collect data from a subprocess. */
83static void
84subprocess (int run, int process_index, void (*func)(unsigned char *))
85{
86 xpthread_barrier_wait (barrier: &shared_data->barrier);
87 func (shared_data->results[run][process_index].bytes);
88}
89
90/* Used to sort the results. */
91struct index
92{
93 int run;
94 int process_index;
95};
96
97/* Used to sort an array of struct index values. */
98static int
99index_compare (const void *left1, const void *right1)
100{
101 const struct index *left = left1;
102 const struct index *right = right1;
103
104 return memcmp (shared_data->results[left->run][left->process_index].bytes,
105 shared_data->results[right->run][right->process_index].bytes,
106 random_size);
107}
108
109static int
110do_test_func (void (*func)(unsigned char *bytes))
111{
112 /* Collect random data. */
113 for (int run = 0; run < runs; ++run)
114 {
115 pid_t pids[subprocesses];
116 for (int process_index = 0; process_index < subprocesses;
117 ++process_index)
118 {
119 pids[process_index] = xfork ();
120 if (pids[process_index] == 0)
121 {
122 subprocess (run, process_index, func);
123 _exit (0);
124 }
125 }
126
127 /* Trigger all subprocesses. Also add data from the parent
128 process. */
129 subprocess (run, process_index: subprocesses, func);
130
131 for (int process_index = 0; process_index < subprocesses;
132 ++process_index)
133 {
134 int status;
135 xwaitpid (pids[process_index], status: &status, flags: 0);
136 if (status != 0)
137 FAIL_EXIT1 ("subprocess index %d (PID %d) exit status %d\n",
138 process_index, (int) pids[process_index], status);
139 }
140 }
141
142 /* Check for duplicates. */
143 struct index indexes[runs * processes];
144 for (int run = 0; run < runs; ++run)
145 for (int process_index = 0; process_index < processes; ++process_index)
146 indexes[run * processes + process_index]
147 = (struct index) { .run = run, .process_index = process_index };
148 qsort (indexes, array_length (indexes), sizeof (indexes[0]), index_compare);
149 for (size_t i = 1; i < array_length (indexes); ++i)
150 {
151 if (index_compare (left1: indexes + i - 1, right1: indexes + i) == 0)
152 {
153 support_record_failure ();
154 unsigned char *bytes
155 = shared_data->results[indexes[i].run]
156 [indexes[i].process_index].bytes;
157 char *quoted = support_quote_blob (blob: bytes, length: random_size);
158 printf (format: "error: duplicate randomness data: \"%s\"\n"
159 " run %d, subprocess %d\n"
160 " run %d, subprocess %d\n",
161 quoted, indexes[i - 1].run, indexes[i - 1].process_index,
162 indexes[i].run, indexes[i].process_index);
163 free (ptr: quoted);
164 }
165 }
166
167 return 0;
168}
169
170static int
171do_test (void)
172{
173 shared_data = support_shared_allocate (size: sizeof (*shared_data));
174 {
175 pthread_barrierattr_t attr;
176 xpthread_barrierattr_init (&attr);
177 xpthread_barrierattr_setpshared (&attr, PTHREAD_PROCESS_SHARED);
178 xpthread_barrier_init (barrier: &shared_data->barrier, attr: &attr, count: processes);
179 xpthread_barrierattr_destroy (&attr);
180 }
181
182 do_test_func (func: generate_arc4random);
183 do_test_func (func: generate_arc4random_buf);
184 do_test_func (func: generate_arc4random_uniform);
185
186 xpthread_barrier_destroy (barrier: &shared_data->barrier);
187 support_shared_free (shared_data);
188 shared_data = NULL;
189
190 return 0;
191}
192
193#define TIMEOUT 40
194#include <support/test-driver.c>
195

source code of glibc/stdlib/tst-arc4random-fork.c