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();

Reply via email to