1 /* Test that subprocesses generate distinct streams of randomness.
2    Copyright (C) 2022 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.  (The number 1500 was
34    seen to reproduce failures reliable in case of a race condition in
35    the fork detection code.)  */
36 enum { runs = 1500 };
37 
38 /* One hundred processes in total.  This should be high enough to
39    expose any issues, but low enough not to tax the overall system too
40    much.  */
41 enum { subprocesses = 49 };
42 
43 /* The total number of processes.  */
44 enum { processes = subprocesses + 1 };
45 
46 /* Number of bytes of randomness to generate per process.  Large
47    enough to make false positive duplicates extremely unlikely.  */
48 enum { random_size = 16 };
49 
50 /* Generated bytes of randomness.  */
51 struct result
52 {
53   unsigned char bytes[random_size];
54 };
55 
56 /* Shared across all processes.  */
57 static struct shared_data
58 {
59   pthread_barrier_t barrier;
60   struct result results[runs][processes];
61 } *shared_data;
62 
63 static void
generate_arc4random(unsigned char * bytes)64 generate_arc4random (unsigned char *bytes)
65 {
66   for (int i = 0; i < random_size / sizeof (uint32_t); i++)
67     {
68       uint32_t x = arc4random ();
69       memcpy (&bytes[4 * i], &x, sizeof x);
70     }
71 }
72 
73 static void
generate_arc4random_buf(unsigned char * bytes)74 generate_arc4random_buf (unsigned char *bytes)
75 {
76   arc4random_buf (bytes, random_size);
77 }
78 
79 static void
generate_arc4random_uniform(unsigned char * bytes)80 generate_arc4random_uniform (unsigned char *bytes)
81 {
82   for (int i = 0; i < random_size; i++)
83     bytes[i] = arc4random_uniform (256);
84 }
85 
86 /* Invoked to collect data from a subprocess.  */
87 static void
subprocess(int run,int process_index,void (* func)(unsigned char *))88 subprocess (int run, int process_index, void (*func)(unsigned char *))
89 {
90   xpthread_barrier_wait (&shared_data->barrier);
91   func (shared_data->results[run][process_index].bytes);
92 }
93 
94 /* Used to sort the results.  */
95 struct index
96 {
97   int run;
98   int process_index;
99 };
100 
101 /* Used to sort an array of struct index values.  */
102 static int
index_compare(const void * left1,const void * right1)103 index_compare (const void *left1, const void *right1)
104 {
105   const struct index *left = left1;
106   const struct index *right = right1;
107 
108   return memcmp (shared_data->results[left->run][left->process_index].bytes,
109                  shared_data->results[right->run][right->process_index].bytes,
110                  random_size);
111 }
112 
113 static int
do_test_func(void (* func)(unsigned char * bytes))114 do_test_func (void (*func)(unsigned char *bytes))
115 {
116   /* Collect random data.  */
117   for (int run = 0; run < runs; ++run)
118     {
119       pid_t pids[subprocesses];
120       for (int process_index = 0; process_index < subprocesses;
121            ++process_index)
122         {
123           pids[process_index] = xfork ();
124           if (pids[process_index] == 0)
125             {
126               subprocess (run, process_index, func);
127               _exit (0);
128             }
129         }
130 
131       /* Trigger all subprocesses.  Also add data from the parent
132          process.  */
133       subprocess (run, subprocesses, func);
134 
135       for (int process_index = 0; process_index < subprocesses;
136            ++process_index)
137         {
138           int status;
139           xwaitpid (pids[process_index], &status, 0);
140           if (status != 0)
141             FAIL_EXIT1 ("subprocess index %d (PID %d) exit status %d\n",
142                         process_index, (int) pids[process_index], status);
143         }
144     }
145 
146   /* Check for duplicates.  */
147   struct index indexes[runs * processes];
148   for (int run = 0; run < runs; ++run)
149     for (int process_index = 0; process_index < processes; ++process_index)
150       indexes[run * processes + process_index]
151         = (struct index) { .run = run, .process_index = process_index };
152   qsort (indexes, array_length (indexes), sizeof (indexes[0]), index_compare);
153   for (size_t i = 1; i < array_length (indexes); ++i)
154     {
155       if (index_compare (indexes + i - 1, indexes + i) == 0)
156         {
157           support_record_failure ();
158           unsigned char *bytes
159             = shared_data->results[indexes[i].run]
160                 [indexes[i].process_index].bytes;
161           char *quoted = support_quote_blob (bytes, random_size);
162           printf ("error: duplicate randomness data: \"%s\"\n"
163                   "  run %d, subprocess %d\n"
164                   "  run %d, subprocess %d\n",
165                   quoted, indexes[i - 1].run, indexes[i - 1].process_index,
166                   indexes[i].run, indexes[i].process_index);
167           free (quoted);
168         }
169     }
170 
171   return 0;
172 }
173 
174 static int
do_test(void)175 do_test (void)
176 {
177   shared_data = support_shared_allocate (sizeof (*shared_data));
178   {
179     pthread_barrierattr_t attr;
180     xpthread_barrierattr_init (&attr);
181     xpthread_barrierattr_setpshared (&attr, PTHREAD_PROCESS_SHARED);
182     xpthread_barrier_init (&shared_data->barrier, &attr, processes);
183     xpthread_barrierattr_destroy (&attr);
184   }
185 
186   do_test_func (generate_arc4random);
187   do_test_func (generate_arc4random_buf);
188   do_test_func (generate_arc4random_uniform);
189 
190   xpthread_barrier_destroy (&shared_data->barrier);
191   support_shared_free (shared_data);
192   shared_data = NULL;
193 
194   return 0;
195 }
196 
197 #define TIMEOUT 40
198 #include <support/test-driver.c>
199