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. */ |
34 | enum { runs = 10 }; |
35 | |
36 | /* Total number of spawned processes on each run. */ |
37 | enum { subprocesses = 49 }; |
38 | |
39 | /* The total number of processes. */ |
40 | enum { processes = subprocesses + 1 }; |
41 | |
42 | /* Number of bytes of randomness to generate per process. Large |
43 | enough to make false positive duplicates extremely unlikely. */ |
44 | enum { random_size = 16 }; |
45 | |
46 | /* Generated bytes of randomness. */ |
47 | struct result |
48 | { |
49 | unsigned char bytes[random_size]; |
50 | }; |
51 | |
52 | /* Shared across all processes. */ |
53 | static struct shared_data |
54 | { |
55 | pthread_barrier_t barrier; |
56 | struct result results[runs][processes]; |
57 | } *shared_data; |
58 | |
59 | static void |
60 | generate_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 | |
69 | static void |
70 | generate_arc4random_buf (unsigned char *bytes) |
71 | { |
72 | arc4random_buf (buf: bytes, size: random_size); |
73 | } |
74 | |
75 | static void |
76 | generate_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. */ |
83 | static void |
84 | subprocess (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. */ |
91 | struct index |
92 | { |
93 | int run; |
94 | int process_index; |
95 | }; |
96 | |
97 | /* Used to sort an array of struct index values. */ |
98 | static int |
99 | index_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 | |
109 | static int |
110 | do_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 | |
170 | static int |
171 | do_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 | |