t_arc4random.c revision 1.3 1 1.3 riastrad /* $NetBSD: t_arc4random.c,v 1.3 2025/03/05 21:30:34 riastradh Exp $ */
2 1.1 riastrad
3 1.1 riastrad /*-
4 1.1 riastrad * Copyright (c) 2024 The NetBSD Foundation, Inc.
5 1.1 riastrad * All rights reserved.
6 1.1 riastrad *
7 1.1 riastrad * Redistribution and use in source and binary forms, with or without
8 1.1 riastrad * modification, are permitted provided that the following conditions
9 1.1 riastrad * are met:
10 1.1 riastrad * 1. Redistributions of source code must retain the above copyright
11 1.1 riastrad * notice, this list of conditions and the following disclaimer.
12 1.1 riastrad * 2. Redistributions in binary form must reproduce the above copyright
13 1.1 riastrad * notice, this list of conditions and the following disclaimer in the
14 1.1 riastrad * documentation and/or other materials provided with the distribution.
15 1.1 riastrad *
16 1.1 riastrad * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 1.1 riastrad * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 1.1 riastrad * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 1.1 riastrad * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 1.1 riastrad * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 1.1 riastrad * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 1.1 riastrad * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 1.1 riastrad * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 1.1 riastrad * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 1.1 riastrad * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 1.1 riastrad * POSSIBILITY OF SUCH DAMAGE.
27 1.1 riastrad */
28 1.1 riastrad
29 1.1 riastrad #define _REENTRANT
30 1.1 riastrad
31 1.1 riastrad #include <sys/cdefs.h>
32 1.3 riastrad __RCSID("$NetBSD: t_arc4random.c,v 1.3 2025/03/05 21:30:34 riastradh Exp $");
33 1.1 riastrad
34 1.1 riastrad #include <sys/resource.h>
35 1.3 riastrad #include <sys/stat.h>
36 1.1 riastrad #include <sys/sysctl.h>
37 1.1 riastrad #include <sys/wait.h>
38 1.1 riastrad
39 1.1 riastrad #include <atf-c.h>
40 1.3 riastrad #include <err.h>
41 1.3 riastrad #include <fcntl.h>
42 1.3 riastrad #include <paths.h>
43 1.1 riastrad #include <stdio.h>
44 1.1 riastrad #include <string.h>
45 1.1 riastrad #include <unistd.h>
46 1.1 riastrad
47 1.1 riastrad #include "arc4random.h"
48 1.1 riastrad #include "reentrant.h"
49 1.1 riastrad #include "h_macros.h"
50 1.1 riastrad
51 1.1 riastrad /*
52 1.1 riastrad * iszero(buf, len)
53 1.1 riastrad *
54 1.1 riastrad * True if len bytes at buf are all zero, false if any one of them
55 1.1 riastrad * is nonzero.
56 1.1 riastrad */
57 1.1 riastrad static bool
58 1.1 riastrad iszero(const void *buf, size_t len)
59 1.1 riastrad {
60 1.1 riastrad const unsigned char *p = buf;
61 1.1 riastrad size_t i;
62 1.1 riastrad
63 1.1 riastrad for (i = 0; i < len; i++) {
64 1.1 riastrad if (p[i] != 0)
65 1.1 riastrad return false;
66 1.1 riastrad }
67 1.1 riastrad return true;
68 1.1 riastrad }
69 1.1 riastrad
70 1.1 riastrad /*
71 1.1 riastrad * arc4random_prng()
72 1.1 riastrad *
73 1.1 riastrad * Get a pointer to the current arc4random state, without updating
74 1.1 riastrad * any of the state, not even lazy initialization.
75 1.1 riastrad */
76 1.1 riastrad static struct arc4random_prng *
77 1.1 riastrad arc4random_prng(void)
78 1.1 riastrad {
79 1.1 riastrad struct arc4random_prng *prng = NULL;
80 1.1 riastrad
81 1.1 riastrad /*
82 1.1 riastrad * If arc4random has been initialized and there is a thread key
83 1.1 riastrad * (i.e., libc was built with _REENTRANT), get the thread-local
84 1.1 riastrad * arc4random state if there is one.
85 1.1 riastrad */
86 1.1 riastrad if (arc4random_global.initialized)
87 1.1 riastrad prng = thr_getspecific(arc4random_global.thread_key);
88 1.1 riastrad
89 1.1 riastrad /*
90 1.1 riastrad * If we couldn't get the thread-local state, get the global
91 1.1 riastrad * state instead.
92 1.1 riastrad */
93 1.1 riastrad if (prng == NULL)
94 1.1 riastrad prng = &arc4random_global.prng;
95 1.1 riastrad
96 1.1 riastrad return prng;
97 1.1 riastrad }
98 1.1 riastrad
99 1.1 riastrad /*
100 1.1 riastrad * arc4random_global_buf(buf, len)
101 1.1 riastrad *
102 1.1 riastrad * Same as arc4random_buf, but force use of the global state.
103 1.1 riastrad * Must happen before any other use of arc4random.
104 1.1 riastrad */
105 1.1 riastrad static void
106 1.1 riastrad arc4random_global_buf(void *buf, size_t len)
107 1.1 riastrad {
108 1.1 riastrad struct rlimit rlim, orlim;
109 1.1 riastrad struct arc4random_prng *prng;
110 1.1 riastrad
111 1.1 riastrad /*
112 1.1 riastrad * Save the address space limit.
113 1.1 riastrad */
114 1.1 riastrad RL(getrlimit(RLIMIT_AS, &orlim));
115 1.1 riastrad memcpy(&rlim, &orlim, sizeof(rlim));
116 1.1 riastrad
117 1.1 riastrad /*
118 1.1 riastrad * Get a sample while the address space limit is zero. This
119 1.1 riastrad * should try, and fail, to allocate a thread-local arc4random
120 1.1 riastrad * state with mmap(2).
121 1.1 riastrad */
122 1.1 riastrad rlim.rlim_cur = 0;
123 1.1 riastrad RL(setrlimit(RLIMIT_AS, &rlim));
124 1.1 riastrad arc4random_buf(buf, len);
125 1.1 riastrad RL(setrlimit(RLIMIT_AS, &orlim));
126 1.1 riastrad
127 1.1 riastrad /*
128 1.1 riastrad * Restore the address space limit.
129 1.1 riastrad */
130 1.1 riastrad RL(setrlimit(RLIMIT_AS, &orlim));
131 1.1 riastrad
132 1.1 riastrad /*
133 1.1 riastrad * Verify the PRNG is the global one, not the thread-local one,
134 1.1 riastrad * and that it was initialized.
135 1.1 riastrad */
136 1.1 riastrad prng = arc4random_prng();
137 1.1 riastrad ATF_CHECK_EQ(prng, &arc4random_global.prng);
138 1.1 riastrad ATF_CHECK(!iszero(&prng->arc4_prng, sizeof(prng->arc4_prng)));
139 1.1 riastrad ATF_CHECK(prng->arc4_epoch != 0);
140 1.1 riastrad }
141 1.1 riastrad
142 1.1 riastrad /*
143 1.1 riastrad * arc4random_global_thread(cookie)
144 1.1 riastrad *
145 1.1 riastrad * Start routine for a thread that just grabs an output from the
146 1.1 riastrad * global state.
147 1.1 riastrad */
148 1.1 riastrad static void *
149 1.1 riastrad arc4random_global_thread(void *cookie)
150 1.1 riastrad {
151 1.1 riastrad unsigned char buf[32];
152 1.1 riastrad
153 1.1 riastrad arc4random_global_buf(buf, sizeof(buf));
154 1.1 riastrad
155 1.1 riastrad return NULL;
156 1.1 riastrad }
157 1.1 riastrad
158 1.1 riastrad ATF_TC(addrandom);
159 1.1 riastrad ATF_TC_HEAD(addrandom, tc)
160 1.1 riastrad {
161 1.1 riastrad atf_tc_set_md_var(tc, "descr",
162 1.1 riastrad "Test arc4random_addrandom updates the state");
163 1.1 riastrad }
164 1.1 riastrad ATF_TC_BODY(addrandom, tc)
165 1.1 riastrad {
166 1.1 riastrad unsigned char buf[32], zero32[32] = {0};
167 1.1 riastrad struct arc4random_prng *prng, copy;
168 1.1 riastrad
169 1.1 riastrad /*
170 1.1 riastrad * Get a sample to start things off.
171 1.1 riastrad */
172 1.1 riastrad arc4random_buf(buf, sizeof(buf));
173 1.1 riastrad ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */
174 1.1 riastrad
175 1.1 riastrad /*
176 1.1 riastrad * By this point, the global state must be initialized -- if
177 1.1 riastrad * not, the process should have aborted.
178 1.1 riastrad */
179 1.1 riastrad ATF_CHECK(arc4random_global.initialized);
180 1.1 riastrad
181 1.1 riastrad /*
182 1.1 riastrad * Get the PRNG, global or local. By this point, the PRNG
183 1.1 riastrad * state should be nonzero (with overwhelmingly high
184 1.1 riastrad * probability) and the epoch should also be nonzero.
185 1.1 riastrad */
186 1.1 riastrad prng = arc4random_prng();
187 1.1 riastrad ATF_CHECK(!iszero(&prng->arc4_prng, sizeof(prng->arc4_prng)));
188 1.1 riastrad ATF_CHECK(prng->arc4_epoch != 0);
189 1.1 riastrad
190 1.1 riastrad /*
191 1.1 riastrad * Save a copy and update the state with arc4random_addrandom.
192 1.1 riastrad */
193 1.1 riastrad copy = *prng;
194 1.1 riastrad arc4random_addrandom(zero32, sizeof(zero32));
195 1.1 riastrad
196 1.1 riastrad /*
197 1.1 riastrad * The state should have changed. (The epoch may or may not.)
198 1.1 riastrad */
199 1.1 riastrad ATF_CHECK(memcmp(&prng->arc4_prng, ©.arc4_prng,
200 1.1 riastrad sizeof(copy.arc4_prng)) != 0);
201 1.1 riastrad
202 1.1 riastrad /*
203 1.1 riastrad * Save a copy and update the state with arc4random_stir.
204 1.1 riastrad */
205 1.1 riastrad copy = *prng;
206 1.1 riastrad arc4random_stir();
207 1.1 riastrad
208 1.1 riastrad /*
209 1.1 riastrad * The state should have changed. (The epoch may or may not.)
210 1.1 riastrad */
211 1.1 riastrad ATF_CHECK(memcmp(&prng->arc4_prng, ©.arc4_prng,
212 1.1 riastrad sizeof(copy.arc4_prng)) != 0);
213 1.1 riastrad }
214 1.1 riastrad
215 1.1 riastrad ATF_TC(consolidate);
216 1.1 riastrad ATF_TC_HEAD(consolidate, tc)
217 1.1 riastrad {
218 1.1 riastrad atf_tc_set_md_var(tc, "descr",
219 1.1 riastrad "Test consolidating entropy resets the epoch");
220 1.1 riastrad }
221 1.1 riastrad ATF_TC_BODY(consolidate, tc)
222 1.1 riastrad {
223 1.1 riastrad unsigned char buf[32];
224 1.1 riastrad struct arc4random_prng *local, *global = &arc4random_global.prng;
225 1.1 riastrad unsigned localepoch, globalepoch;
226 1.1 riastrad const int consolidate = 1;
227 1.1 riastrad pthread_t thread;
228 1.1 riastrad
229 1.1 riastrad /*
230 1.1 riastrad * Get a sample from the global state to make sure the global
231 1.1 riastrad * state is initialized. Remember the epoch.
232 1.1 riastrad */
233 1.1 riastrad arc4random_global_buf(buf, sizeof(buf));
234 1.1 riastrad ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */
235 1.1 riastrad ATF_CHECK(!iszero(&global->arc4_prng, sizeof(global->arc4_prng)));
236 1.1 riastrad ATF_CHECK((globalepoch = global->arc4_epoch) != 0);
237 1.1 riastrad
238 1.1 riastrad /*
239 1.1 riastrad * Get a sample from the local state too to make sure the local
240 1.1 riastrad * state is initialized. Remember the epoch.
241 1.1 riastrad */
242 1.1 riastrad arc4random_buf(buf, sizeof(buf));
243 1.1 riastrad ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */
244 1.1 riastrad local = arc4random_prng();
245 1.1 riastrad ATF_CHECK(!iszero(&local->arc4_prng, sizeof(local->arc4_prng)));
246 1.1 riastrad ATF_CHECK((localepoch = local->arc4_epoch) != 0);
247 1.1 riastrad
248 1.1 riastrad /*
249 1.1 riastrad * Trigger entropy consolidation.
250 1.1 riastrad */
251 1.1 riastrad RL(sysctlbyname("kern.entropy.consolidate", /*oldp*/NULL, /*oldlen*/0,
252 1.1 riastrad &consolidate, sizeof(consolidate)));
253 1.1 riastrad
254 1.1 riastrad /*
255 1.1 riastrad * Verify the epoch cache isn't changed yet until we ask for
256 1.1 riastrad * more data.
257 1.1 riastrad */
258 1.1 riastrad ATF_CHECK_EQ_MSG(globalepoch, global->arc4_epoch,
259 1.1 riastrad "global epoch was %u, now %u", globalepoch, global->arc4_epoch);
260 1.1 riastrad ATF_CHECK_EQ_MSG(localepoch, local->arc4_epoch,
261 1.1 riastrad "local epoch was %u, now %u", localepoch, local->arc4_epoch);
262 1.1 riastrad
263 1.1 riastrad /*
264 1.1 riastrad * Request new output and verify the local epoch cache has
265 1.1 riastrad * changed.
266 1.1 riastrad */
267 1.1 riastrad arc4random_buf(buf, sizeof(buf));
268 1.1 riastrad ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */
269 1.1 riastrad ATF_CHECK_MSG(localepoch != local->arc4_epoch,
270 1.1 riastrad "local epoch unchanged from %u", localepoch);
271 1.1 riastrad
272 1.1 riastrad /*
273 1.1 riastrad * Create a new thread to grab output from the global state,
274 1.1 riastrad * wait for it to complete, and verify the global epoch cache
275 1.1 riastrad * has changed. (Now that we have already used the local state
276 1.1 riastrad * in this thread, we can't use the global state any more.)
277 1.1 riastrad */
278 1.1 riastrad RZ(pthread_create(&thread, NULL, &arc4random_global_thread, NULL));
279 1.1 riastrad RZ(pthread_join(thread, NULL));
280 1.1 riastrad ATF_CHECK_MSG(globalepoch != global->arc4_epoch,
281 1.1 riastrad "global epoch unchanged from %u", globalepoch);
282 1.1 riastrad }
283 1.1 riastrad
284 1.3 riastrad ATF_TC(chroot);
285 1.3 riastrad ATF_TC_HEAD(chroot, tc)
286 1.3 riastrad {
287 1.3 riastrad atf_tc_set_md_var(tc, "descr",
288 1.3 riastrad "Test arc4random in an empty chroot");
289 1.3 riastrad atf_tc_set_md_var(tc, "require.user", "root");
290 1.3 riastrad }
291 1.3 riastrad ATF_TC_BODY(chroot, tc)
292 1.3 riastrad {
293 1.3 riastrad pid_t pid;
294 1.3 riastrad int status;
295 1.3 riastrad
296 1.3 riastrad /*
297 1.3 riastrad * Create an empty chroot.
298 1.3 riastrad */
299 1.3 riastrad RL(mkdir("root", 0500));
300 1.3 riastrad
301 1.3 riastrad /*
302 1.3 riastrad * In a child process, enter the chroot and verify that we
303 1.3 riastrad * can't open /dev/urandom but we can use arc4random.
304 1.3 riastrad *
305 1.3 riastrad * (atf gets unhappy if we chroot in the same process, when it
306 1.3 riastrad * later tries to create a results file.)
307 1.3 riastrad */
308 1.3 riastrad RL(pid = fork());
309 1.3 riastrad if (pid == 0) {
310 1.3 riastrad unsigned char buf[32] = {0};
311 1.3 riastrad
312 1.3 riastrad if (chroot("root") == -1)
313 1.3 riastrad err(1, "chroot");
314 1.3 riastrad if (open(_PATH_URANDOM, O_RDONLY) != -1)
315 1.3 riastrad errx(1, "open /dev/urandom must fail in empty chroot");
316 1.3 riastrad if (errno != ENOENT) {
317 1.3 riastrad err(1, "expected open to fail with %d=ENOENT, not %d",
318 1.3 riastrad ENOENT, errno);
319 1.3 riastrad }
320 1.3 riastrad arc4random_buf(buf, sizeof(buf));
321 1.3 riastrad if (iszero(buf, sizeof(buf))) /* Pr[fail] = 1/2^256 */
322 1.3 riastrad errx(1, "arc4random returned all-zero");
323 1.3 riastrad if (arc4random_prng()->arc4_epoch == 0)
324 1.3 riastrad errx(1, "arc4random failed to observe entropy epoch");
325 1.3 riastrad _exit(0);
326 1.3 riastrad }
327 1.3 riastrad
328 1.3 riastrad /*
329 1.3 riastrad * Wait for the child process to finish.
330 1.3 riastrad */
331 1.3 riastrad RL(waitpid(pid, &status, 0));
332 1.3 riastrad ATF_CHECK_MSG(WIFEXITED(status) && WEXITSTATUS(status) == 0,
333 1.3 riastrad "child exited status 0x%x", status);
334 1.3 riastrad }
335 1.3 riastrad
336 1.3 riastrad ATF_TC(fdlimit);
337 1.3 riastrad ATF_TC_HEAD(fdlimit, tc)
338 1.3 riastrad {
339 1.3 riastrad atf_tc_set_md_var(tc, "descr",
340 1.3 riastrad "Test arc4random works even if we have hit the fd limit");
341 1.3 riastrad }
342 1.3 riastrad ATF_TC_BODY(fdlimit, tc)
343 1.3 riastrad {
344 1.3 riastrad pid_t pid;
345 1.3 riastrad int status;
346 1.3 riastrad
347 1.3 riastrad /*
348 1.3 riastrad * In a child process, clamp down on the file descriptor
349 1.3 riastrad * resource limit and verify that we can't open /dev/urandom
350 1.3 riastrad * but we can use arc4random.
351 1.3 riastrad *
352 1.3 riastrad * (atf gets unhappy if we chroot in the same process, when it
353 1.3 riastrad * later tries to create a results file.)
354 1.3 riastrad */
355 1.3 riastrad RL(pid = fork());
356 1.3 riastrad if (pid == 0) {
357 1.3 riastrad struct rlimit rlim = {.rlim_cur = 0, .rlim_max = 0};
358 1.3 riastrad unsigned char buf[32] = {0};
359 1.3 riastrad
360 1.3 riastrad if (setrlimit(RLIMIT_NOFILE, &rlim) == -1)
361 1.3 riastrad err(1, "setrlimit(RLIMIT_NOFILE)");
362 1.3 riastrad if (open(_PATH_URANDOM, O_RDONLY) != -1)
363 1.3 riastrad errx(1, "open must fail with zero RLIMIT_NOFILE");
364 1.3 riastrad if (errno != EMFILE) {
365 1.3 riastrad err(1, "expected open to fail with %d=EMFILE, not %d",
366 1.3 riastrad EMFILE, errno);
367 1.3 riastrad }
368 1.3 riastrad arc4random_buf(buf, sizeof(buf));
369 1.3 riastrad if (iszero(buf, sizeof(buf))) /* Pr[fail] = 1/2^256 */
370 1.3 riastrad errx(1, "arc4random returned all-zero");
371 1.3 riastrad if (arc4random_prng()->arc4_epoch == 0)
372 1.3 riastrad errx(1, "arc4random failed to observe entropy epoch");
373 1.3 riastrad _exit(0);
374 1.3 riastrad }
375 1.3 riastrad
376 1.3 riastrad /*
377 1.3 riastrad * Wait for the child process to finish.
378 1.3 riastrad */
379 1.3 riastrad RL(waitpid(pid, &status, 0));
380 1.3 riastrad ATF_CHECK_MSG(WIFEXITED(status) && WEXITSTATUS(status) == 0,
381 1.3 riastrad "child exited status 0x%x", status);
382 1.3 riastrad }
383 1.3 riastrad
384 1.1 riastrad ATF_TC(fork);
385 1.1 riastrad ATF_TC_HEAD(fork, tc)
386 1.1 riastrad {
387 1.1 riastrad atf_tc_set_md_var(tc, "descr",
388 1.1 riastrad "Test fork zeros the state and gets independent state");
389 1.1 riastrad }
390 1.1 riastrad ATF_TC_BODY(fork, tc)
391 1.1 riastrad {
392 1.1 riastrad unsigned char buf[32];
393 1.1 riastrad struct arc4random_prng *local, *global = &arc4random_global.prng;
394 1.1 riastrad struct arc4random_prng childstate;
395 1.1 riastrad int fd[2];
396 1.1 riastrad pid_t child, pid;
397 1.1 riastrad ssize_t nread;
398 1.1 riastrad int status;
399 1.1 riastrad
400 1.1 riastrad /*
401 1.1 riastrad * Get a sample from the global state to make sure the global
402 1.1 riastrad * state is initialized.
403 1.1 riastrad */
404 1.1 riastrad arc4random_global_buf(buf, sizeof(buf));
405 1.1 riastrad ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */
406 1.1 riastrad ATF_CHECK(!iszero(&global->arc4_prng, sizeof(global->arc4_prng)));
407 1.1 riastrad ATF_CHECK(global->arc4_epoch != 0);
408 1.1 riastrad
409 1.1 riastrad /*
410 1.1 riastrad * Get a sample from the local state too to make sure the local
411 1.1 riastrad * state is initialized.
412 1.1 riastrad */
413 1.1 riastrad arc4random_buf(buf, sizeof(buf));
414 1.1 riastrad ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */
415 1.1 riastrad local = arc4random_prng();
416 1.1 riastrad ATF_CHECK(!iszero(&local->arc4_prng, sizeof(local->arc4_prng)));
417 1.1 riastrad ATF_CHECK(local->arc4_epoch != 0);
418 1.1 riastrad
419 1.1 riastrad /*
420 1.1 riastrad * Create a pipe to transfer the state from child to parent.
421 1.1 riastrad */
422 1.1 riastrad RL(pipe(fd));
423 1.1 riastrad
424 1.1 riastrad /*
425 1.1 riastrad * Fork a child.
426 1.1 riastrad */
427 1.1 riastrad RL(child = fork());
428 1.1 riastrad if (child == 0) {
429 1.1 riastrad status = 0;
430 1.1 riastrad
431 1.1 riastrad /*
432 1.1 riastrad * Verify the states have been zero'd on fork.
433 1.1 riastrad */
434 1.1 riastrad if (!iszero(local, sizeof(*local))) {
435 1.1 riastrad fprintf(stderr, "failed to zero local state\n");
436 1.1 riastrad status = 1;
437 1.1 riastrad }
438 1.1 riastrad if (!iszero(global, sizeof(*global))) {
439 1.1 riastrad fprintf(stderr, "failed to zero global state\n");
440 1.1 riastrad status = 1;
441 1.1 riastrad }
442 1.1 riastrad
443 1.1 riastrad /*
444 1.1 riastrad * Verify we generate nonzero output.
445 1.1 riastrad */
446 1.1 riastrad arc4random_buf(buf, sizeof(buf));
447 1.1 riastrad if (iszero(buf, sizeof(buf))) {
448 1.1 riastrad fprintf(stderr, "failed to generate nonzero output\n");
449 1.1 riastrad status = 1;
450 1.1 riastrad }
451 1.1 riastrad
452 1.1 riastrad /*
453 1.1 riastrad * Share the state to compare with parent.
454 1.1 riastrad */
455 1.1 riastrad if ((size_t)write(fd[1], local, sizeof(*local)) !=
456 1.1 riastrad sizeof(*local)) {
457 1.1 riastrad fprintf(stderr, "failed to share local state\n");
458 1.1 riastrad status = 1;
459 1.1 riastrad }
460 1.1 riastrad _exit(status);
461 1.1 riastrad }
462 1.1 riastrad
463 1.1 riastrad /*
464 1.1 riastrad * Verify the global state has been zeroed as expected. (This
465 1.1 riastrad * way it is never available to the child, even shortly after
466 1.1 riastrad * the fork syscall returns before the atfork handler is
467 1.1 riastrad * called.)
468 1.1 riastrad */
469 1.1 riastrad ATF_CHECK(iszero(global, sizeof(*global)));
470 1.1 riastrad
471 1.1 riastrad /*
472 1.1 riastrad * Read the state from the child.
473 1.1 riastrad */
474 1.1 riastrad RL(nread = read(fd[0], &childstate, sizeof(childstate)));
475 1.1 riastrad ATF_CHECK_EQ_MSG(nread, sizeof(childstate),
476 1.1 riastrad "nread=%zu sizeof(childstate)=%zu", nread, sizeof(childstate));
477 1.1 riastrad
478 1.1 riastrad /*
479 1.1 riastrad * Verify the child state is distinct. (The global state has
480 1.1 riastrad * been zero'd so it's OK it if coincides.) Check again after
481 1.1 riastrad * we grab another output.
482 1.1 riastrad */
483 1.1 riastrad ATF_CHECK(memcmp(local, &childstate, sizeof(*local)) != 0);
484 1.1 riastrad arc4random_buf(buf, sizeof(buf));
485 1.1 riastrad ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */
486 1.1 riastrad ATF_CHECK(memcmp(local, &childstate, sizeof(*local)) != 0);
487 1.1 riastrad
488 1.1 riastrad /*
489 1.1 riastrad * Wait for the child to complete and verify it passed.
490 1.1 riastrad */
491 1.1 riastrad RL(pid = waitpid(child, &status, 0));
492 1.1 riastrad ATF_CHECK_EQ_MSG(status, 0, "child exited with nonzero status=%d",
493 1.1 riastrad status);
494 1.1 riastrad }
495 1.1 riastrad
496 1.2 riastrad ATF_TC(global_aslimit);
497 1.2 riastrad ATF_TC_HEAD(global_aslimit, tc)
498 1.1 riastrad {
499 1.1 riastrad atf_tc_set_md_var(tc, "descr",
500 1.1 riastrad "Test the global state is used when address space limit is hit");
501 1.1 riastrad }
502 1.2 riastrad ATF_TC_BODY(global_aslimit, tc)
503 1.1 riastrad {
504 1.1 riastrad unsigned char buf[32], buf1[32];
505 1.1 riastrad
506 1.1 riastrad /*
507 1.1 riastrad * Get a sample from the global state (and verify it was using
508 1.1 riastrad * the global state).
509 1.1 riastrad */
510 1.1 riastrad arc4random_global_buf(buf, sizeof(buf));
511 1.1 riastrad
512 1.1 riastrad /*
513 1.1 riastrad * Verify we got a sample.
514 1.1 riastrad */
515 1.1 riastrad ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */
516 1.1 riastrad
517 1.1 riastrad /*
518 1.1 riastrad * Get a sample from whatever state and make sure it wasn't
519 1.1 riastrad * repeated, which happens only with probability 1/2^256.
520 1.1 riastrad */
521 1.1 riastrad arc4random_buf(buf1, sizeof(buf1));
522 1.1 riastrad ATF_CHECK(!iszero(buf1, sizeof(buf1))); /* Pr[fail] = 1/2^256 */
523 1.1 riastrad ATF_CHECK(memcmp(buf, buf1, sizeof(buf)) != 0);
524 1.1 riastrad }
525 1.1 riastrad
526 1.2 riastrad ATF_TC(global_threadkeylimit);
527 1.2 riastrad ATF_TC_HEAD(global_threadkeylimit, tc)
528 1.2 riastrad {
529 1.2 riastrad atf_tc_set_md_var(tc, "descr",
530 1.2 riastrad "Test the global state is used we run out of thread keys");
531 1.2 riastrad }
532 1.2 riastrad ATF_TC_BODY(global_threadkeylimit, tc)
533 1.2 riastrad {
534 1.2 riastrad unsigned char buf[32], buf1[32];
535 1.2 riastrad
536 1.2 riastrad /*
537 1.2 riastrad * Get a sample from the global state (and verify it was using
538 1.2 riastrad * the global state).
539 1.2 riastrad */
540 1.2 riastrad arc4random_global_buf(buf, sizeof(buf));
541 1.2 riastrad
542 1.2 riastrad /*
543 1.2 riastrad * Verify we got a sample.
544 1.2 riastrad */
545 1.2 riastrad ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */
546 1.2 riastrad
547 1.2 riastrad /*
548 1.2 riastrad * Artificially disable the per-thread state and clear the
549 1.2 riastrad * epoch.
550 1.2 riastrad */
551 1.2 riastrad arc4random_global.per_thread = false;
552 1.2 riastrad arc4random_global.prng.arc4_epoch = 0;
553 1.2 riastrad
554 1.2 riastrad /*
555 1.2 riastrad * Get a sample again and make sure it wasn't repeated, which
556 1.2 riastrad * happens only with probability 1/2^256.
557 1.2 riastrad */
558 1.2 riastrad arc4random_buf(buf1, sizeof(buf1));
559 1.2 riastrad ATF_CHECK(!iszero(buf1, sizeof(buf1))); /* Pr[fail] = 1/2^256 */
560 1.2 riastrad ATF_CHECK(memcmp(buf, buf1, sizeof(buf)) != 0);
561 1.2 riastrad
562 1.2 riastrad /*
563 1.2 riastrad * Verify this had the effect of updating the global epoch,
564 1.2 riastrad * meaning we used the global state and not the per-thread
565 1.2 riastrad * state.
566 1.2 riastrad */
567 1.2 riastrad ATF_CHECK(arc4random_global.prng.arc4_epoch != 0);
568 1.2 riastrad }
569 1.2 riastrad
570 1.1 riastrad ATF_TC(local);
571 1.1 riastrad ATF_TC_HEAD(local, tc)
572 1.1 riastrad {
573 1.1 riastrad atf_tc_set_md_var(tc, "descr",
574 1.1 riastrad "Test arc4random uses thread-local state");
575 1.1 riastrad /* XXX skip if libc was built without _REENTRANT */
576 1.1 riastrad }
577 1.1 riastrad ATF_TC_BODY(local, tc)
578 1.1 riastrad {
579 1.1 riastrad unsigned char buf[32], buf1[32];
580 1.1 riastrad struct arc4random_prng *prng;
581 1.1 riastrad
582 1.1 riastrad /*
583 1.1 riastrad * Get a sample to start things off.
584 1.1 riastrad */
585 1.1 riastrad arc4random_buf(buf, sizeof(buf));
586 1.1 riastrad ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */
587 1.1 riastrad
588 1.1 riastrad /*
589 1.1 riastrad * Verify the arc4random state is _not_ the global state.
590 1.1 riastrad */
591 1.1 riastrad prng = arc4random_prng();
592 1.1 riastrad ATF_CHECK(prng != &arc4random_global.prng);
593 1.1 riastrad ATF_CHECK(!iszero(&prng->arc4_prng, sizeof(prng->arc4_prng)));
594 1.1 riastrad ATF_CHECK(prng->arc4_epoch != 0);
595 1.1 riastrad
596 1.1 riastrad /*
597 1.1 riastrad * Get another sample and make sure it wasn't repeated, which
598 1.1 riastrad * happens only with probability 1/2^256.
599 1.1 riastrad */
600 1.1 riastrad arc4random_buf(buf1, sizeof(buf1));
601 1.1 riastrad ATF_CHECK(!iszero(buf1, sizeof(buf1))); /* Pr[fail] = 1/2^256 */
602 1.1 riastrad ATF_CHECK(memcmp(buf, buf1, sizeof(buf)) != 0);
603 1.1 riastrad }
604 1.1 riastrad
605 1.1 riastrad ATF_TP_ADD_TCS(tp)
606 1.1 riastrad {
607 1.1 riastrad
608 1.1 riastrad ATF_TP_ADD_TC(tp, addrandom);
609 1.3 riastrad ATF_TP_ADD_TC(tp, chroot);
610 1.1 riastrad ATF_TP_ADD_TC(tp, consolidate);
611 1.3 riastrad ATF_TP_ADD_TC(tp, fdlimit);
612 1.1 riastrad ATF_TP_ADD_TC(tp, fork);
613 1.2 riastrad ATF_TP_ADD_TC(tp, global_aslimit);
614 1.2 riastrad ATF_TP_ADD_TC(tp, global_threadkeylimit);
615 1.1 riastrad ATF_TP_ADD_TC(tp, local);
616 1.1 riastrad
617 1.1 riastrad return atf_no_error();
618 1.1 riastrad }
619