Module Name: src Committed By: riastradh Date: Sun Mar 2 21:35:59 UTC 2025
Modified Files: src/lib/libc/gen: arc4random.c src/lib/libc/include: arc4random.h src/tests/lib/libc/gen: t_arc4random.c Log Message: arc4random(3): Avoid failure due to thread key limits. If thr_keycreate (a.k.a. pthread_key_create) fails, fall back to using globally serialized state instead of per-thread state. This is unlikely to happen but arc4random(3) should work even if it does. New test case forces exercising this path (at least, simulating the effect of key creation failure). PR lib/59117: arc4random has some failure modes it shouldn't To generate a diff of this commit: cvs rdiff -u -r1.38 -r1.39 src/lib/libc/gen/arc4random.c cvs rdiff -u -r1.1 -r1.2 src/lib/libc/include/arc4random.h cvs rdiff -u -r1.1 -r1.2 src/tests/lib/libc/gen/t_arc4random.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/lib/libc/gen/arc4random.c diff -u src/lib/libc/gen/arc4random.c:1.38 src/lib/libc/gen/arc4random.c:1.39 --- src/lib/libc/gen/arc4random.c:1.38 Thu Aug 29 13:39:42 2024 +++ src/lib/libc/gen/arc4random.c Sun Mar 2 21:35:59 2025 @@ -1,4 +1,4 @@ -/* $NetBSD: arc4random.c,v 1.38 2024/08/29 13:39:42 riastradh Exp $ */ +/* $NetBSD: arc4random.c,v 1.39 2025/03/02 21:35:59 riastradh Exp $ */ /*- * Copyright (c) 2014 The NetBSD Foundation, Inc. @@ -52,7 +52,7 @@ */ #include <sys/cdefs.h> -__RCSID("$NetBSD: arc4random.c,v 1.38 2024/08/29 13:39:42 riastradh Exp $"); +__RCSID("$NetBSD: arc4random.c,v 1.39 2025/03/02 21:35:59 riastradh Exp $"); #include "namespace.h" #include "reentrant.h" @@ -581,8 +581,8 @@ arc4random_initialize(void) abort(); #ifdef _REENTRANT if (thr_keycreate(&arc4random_global.thread_key, - &arc4random_tsd_destructor) != 0) - abort(); + &arc4random_tsd_destructor) == 0) + arc4random_global.per_thread = true; #endif arc4random_global.initialized = true; } @@ -600,8 +600,10 @@ arc4random_prng_get(void) #ifdef _REENTRANT /* Get or create the per-thread PRNG state. */ - prng = thr_getspecific(arc4random_global.thread_key); - if (__predict_false(prng == NULL)) { + prng = __predict_true(arc4random_global.per_thread) + ? thr_getspecific(arc4random_global.thread_key) + : NULL; + if (__predict_false(prng == NULL) && arc4random_global.per_thread) { prng = arc4random_prng_create(); thr_setspecific(arc4random_global.thread_key, prng); } Index: src/lib/libc/include/arc4random.h diff -u src/lib/libc/include/arc4random.h:1.1 src/lib/libc/include/arc4random.h:1.2 --- src/lib/libc/include/arc4random.h:1.1 Tue Aug 27 13:43:02 2024 +++ src/lib/libc/include/arc4random.h Sun Mar 2 21:35:59 2025 @@ -1,4 +1,4 @@ -/* $NetBSD: arc4random.h,v 1.1 2024/08/27 13:43:02 riastradh Exp $ */ +/* $NetBSD: arc4random.h,v 1.2 2025/03/02 21:35:59 riastradh Exp $ */ /*- * Copyright (c) 2014 The NetBSD Foundation, Inc. @@ -51,6 +51,7 @@ struct arc4random_global_state { thread_key_t thread_key; struct arc4random_prng prng; bool initialized; + bool per_thread; }; #define arc4random_global __arc4random_global /* libc private symbol */ Index: src/tests/lib/libc/gen/t_arc4random.c diff -u src/tests/lib/libc/gen/t_arc4random.c:1.1 src/tests/lib/libc/gen/t_arc4random.c:1.2 --- src/tests/lib/libc/gen/t_arc4random.c:1.1 Tue Aug 27 13:43:02 2024 +++ src/tests/lib/libc/gen/t_arc4random.c Sun Mar 2 21:35:59 2025 @@ -1,4 +1,4 @@ -/* $NetBSD: t_arc4random.c,v 1.1 2024/08/27 13:43:02 riastradh Exp $ */ +/* $NetBSD: t_arc4random.c,v 1.2 2025/03/02 21:35:59 riastradh Exp $ */ /*- * Copyright (c) 2024 The NetBSD Foundation, Inc. @@ -29,7 +29,7 @@ #define _REENTRANT #include <sys/cdefs.h> -__RCSID("$NetBSD: t_arc4random.c,v 1.1 2024/08/27 13:43:02 riastradh Exp $"); +__RCSID("$NetBSD: t_arc4random.c,v 1.2 2025/03/02 21:35:59 riastradh Exp $"); #include <sys/resource.h> #include <sys/sysctl.h> @@ -389,13 +389,13 @@ ATF_TC_BODY(fork, tc) status); } -ATF_TC(global); -ATF_TC_HEAD(global, tc) +ATF_TC(global_aslimit); +ATF_TC_HEAD(global_aslimit, tc) { atf_tc_set_md_var(tc, "descr", "Test the global state is used when address space limit is hit"); } -ATF_TC_BODY(global, tc) +ATF_TC_BODY(global_aslimit, tc) { unsigned char buf[32], buf1[32]; @@ -419,6 +419,50 @@ ATF_TC_BODY(global, tc) ATF_CHECK(memcmp(buf, buf1, sizeof(buf)) != 0); } +ATF_TC(global_threadkeylimit); +ATF_TC_HEAD(global_threadkeylimit, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test the global state is used we run out of thread keys"); +} +ATF_TC_BODY(global_threadkeylimit, tc) +{ + unsigned char buf[32], buf1[32]; + + /* + * Get a sample from the global state (and verify it was using + * the global state). + */ + arc4random_global_buf(buf, sizeof(buf)); + + /* + * Verify we got a sample. + */ + ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */ + + /* + * Artificially disable the per-thread state and clear the + * epoch. + */ + arc4random_global.per_thread = false; + arc4random_global.prng.arc4_epoch = 0; + + /* + * Get a sample again and make sure it wasn't repeated, which + * happens only with probability 1/2^256. + */ + arc4random_buf(buf1, sizeof(buf1)); + ATF_CHECK(!iszero(buf1, sizeof(buf1))); /* Pr[fail] = 1/2^256 */ + ATF_CHECK(memcmp(buf, buf1, sizeof(buf)) != 0); + + /* + * Verify this had the effect of updating the global epoch, + * meaning we used the global state and not the per-thread + * state. + */ + ATF_CHECK(arc4random_global.prng.arc4_epoch != 0); +} + ATF_TC(local); ATF_TC_HEAD(local, tc) { @@ -460,7 +504,8 @@ ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TC(tp, addrandom); ATF_TP_ADD_TC(tp, consolidate); ATF_TP_ADD_TC(tp, fork); - ATF_TP_ADD_TC(tp, global); + ATF_TP_ADD_TC(tp, global_aslimit); + ATF_TP_ADD_TC(tp, global_threadkeylimit); ATF_TP_ADD_TC(tp, local); return atf_no_error();