1 1.5 riastrad /* $NetBSD: t_arc4random.c,v 1.5 2025/03/09 18:11:55 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.5 riastrad __RCSID("$NetBSD: t_arc4random.c,v 1.5 2025/03/09 18:11:55 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.4 riastrad if (arc4random_global.per_thread) 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.4 riastrad * Artificially disable the per-thread state, make it an 549 1.4 riastrad * invalid thread key altogether, and clear the epoch. Make 550 1.4 riastrad * sure we're using the global PRNG state now. 551 1.2 riastrad */ 552 1.2 riastrad arc4random_global.per_thread = false; 553 1.4 riastrad memset(&arc4random_global.thread_key, 0x5a, 554 1.4 riastrad sizeof(arc4random_global.thread_key)); 555 1.2 riastrad arc4random_global.prng.arc4_epoch = 0; 556 1.4 riastrad ATF_CHECK(arc4random_prng() == &arc4random_global.prng); 557 1.2 riastrad 558 1.2 riastrad /* 559 1.2 riastrad * Get a sample again and make sure it wasn't repeated, which 560 1.2 riastrad * happens only with probability 1/2^256. 561 1.2 riastrad */ 562 1.2 riastrad arc4random_buf(buf1, sizeof(buf1)); 563 1.2 riastrad ATF_CHECK(!iszero(buf1, sizeof(buf1))); /* Pr[fail] = 1/2^256 */ 564 1.2 riastrad ATF_CHECK(memcmp(buf, buf1, sizeof(buf)) != 0); 565 1.2 riastrad 566 1.2 riastrad /* 567 1.2 riastrad * Verify this had the effect of updating the global epoch, 568 1.2 riastrad * meaning we used the global state and not the per-thread 569 1.2 riastrad * state. 570 1.2 riastrad */ 571 1.2 riastrad ATF_CHECK(arc4random_global.prng.arc4_epoch != 0); 572 1.2 riastrad } 573 1.2 riastrad 574 1.1 riastrad ATF_TC(local); 575 1.1 riastrad ATF_TC_HEAD(local, tc) 576 1.1 riastrad { 577 1.1 riastrad atf_tc_set_md_var(tc, "descr", 578 1.1 riastrad "Test arc4random uses thread-local state"); 579 1.1 riastrad /* XXX skip if libc was built without _REENTRANT */ 580 1.1 riastrad } 581 1.1 riastrad ATF_TC_BODY(local, tc) 582 1.1 riastrad { 583 1.1 riastrad unsigned char buf[32], buf1[32]; 584 1.1 riastrad struct arc4random_prng *prng; 585 1.1 riastrad 586 1.1 riastrad /* 587 1.1 riastrad * Get a sample to start things off. 588 1.1 riastrad */ 589 1.1 riastrad arc4random_buf(buf, sizeof(buf)); 590 1.1 riastrad ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */ 591 1.1 riastrad 592 1.1 riastrad /* 593 1.1 riastrad * Verify the arc4random state is _not_ the global state. 594 1.1 riastrad */ 595 1.1 riastrad prng = arc4random_prng(); 596 1.1 riastrad ATF_CHECK(prng != &arc4random_global.prng); 597 1.1 riastrad ATF_CHECK(!iszero(&prng->arc4_prng, sizeof(prng->arc4_prng))); 598 1.1 riastrad ATF_CHECK(prng->arc4_epoch != 0); 599 1.1 riastrad 600 1.1 riastrad /* 601 1.1 riastrad * Get another sample and make sure it wasn't repeated, which 602 1.1 riastrad * happens only with probability 1/2^256. 603 1.1 riastrad */ 604 1.1 riastrad arc4random_buf(buf1, sizeof(buf1)); 605 1.1 riastrad ATF_CHECK(!iszero(buf1, sizeof(buf1))); /* Pr[fail] = 1/2^256 */ 606 1.1 riastrad ATF_CHECK(memcmp(buf, buf1, sizeof(buf)) != 0); 607 1.1 riastrad } 608 1.1 riastrad 609 1.5 riastrad ATF_TC(stackfallback); 610 1.5 riastrad ATF_TC_HEAD(stackfallback, tc) 611 1.5 riastrad { 612 1.5 riastrad atf_tc_set_md_var(tc, "descr", 613 1.5 riastrad "Test arc4random with pthread_atfork and thr_keycreate failure"); 614 1.5 riastrad } 615 1.5 riastrad ATF_TC_BODY(stackfallback, tc) 616 1.5 riastrad { 617 1.5 riastrad unsigned char buf[32], buf1[32]; 618 1.5 riastrad struct arc4random_prng *local; 619 1.5 riastrad 620 1.5 riastrad /* 621 1.5 riastrad * Get a sample to start things off. This makes the library 622 1.5 riastrad * gets initialized. 623 1.5 riastrad */ 624 1.5 riastrad arc4random_buf(buf, sizeof(buf)); 625 1.5 riastrad ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */ 626 1.5 riastrad 627 1.5 riastrad /* 628 1.5 riastrad * Clear the arc4random global state, and the local state if it 629 1.5 riastrad * exists, and pretend pthread_atfork and thr_keycreate had 630 1.5 riastrad * both failed. 631 1.5 riastrad */ 632 1.5 riastrad memset(&arc4random_global.prng, 0, sizeof(arc4random_global.prng)); 633 1.5 riastrad if ((local = arc4random_prng()) != NULL) 634 1.5 riastrad memset(local, 0, sizeof(*local)); 635 1.5 riastrad arc4random_global.forksafe = false; 636 1.5 riastrad arc4random_global.per_thread = false; 637 1.5 riastrad 638 1.5 riastrad /* 639 1.5 riastrad * Make sure it still works to get a sample. 640 1.5 riastrad */ 641 1.5 riastrad arc4random_buf(buf1, sizeof(buf1)); 642 1.5 riastrad ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */ 643 1.5 riastrad ATF_CHECK(memcmp(buf, buf1, sizeof(buf)) != 0); 644 1.5 riastrad 645 1.5 riastrad /* 646 1.5 riastrad * Make sure the global and local epochs did not change. 647 1.5 riastrad */ 648 1.5 riastrad ATF_CHECK_EQ_MSG(arc4random_global.prng.arc4_epoch, 0, 649 1.5 riastrad "global epoch: %d", arc4random_global.prng.arc4_epoch); 650 1.5 riastrad if (local != NULL) { 651 1.5 riastrad ATF_CHECK_EQ_MSG(local->arc4_epoch, 0, 652 1.5 riastrad "local epoch: %d", local->arc4_epoch); 653 1.5 riastrad } 654 1.5 riastrad } 655 1.5 riastrad 656 1.1 riastrad ATF_TP_ADD_TCS(tp) 657 1.1 riastrad { 658 1.1 riastrad 659 1.1 riastrad ATF_TP_ADD_TC(tp, addrandom); 660 1.3 riastrad ATF_TP_ADD_TC(tp, chroot); 661 1.1 riastrad ATF_TP_ADD_TC(tp, consolidate); 662 1.3 riastrad ATF_TP_ADD_TC(tp, fdlimit); 663 1.1 riastrad ATF_TP_ADD_TC(tp, fork); 664 1.2 riastrad ATF_TP_ADD_TC(tp, global_aslimit); 665 1.2 riastrad ATF_TP_ADD_TC(tp, global_threadkeylimit); 666 1.1 riastrad ATF_TP_ADD_TC(tp, local); 667 1.5 riastrad ATF_TP_ADD_TC(tp, stackfallback); 668 1.1 riastrad 669 1.1 riastrad return atf_no_error(); 670 1.1 riastrad } 671