Module Name:    src
Committed By:   riastradh
Date:           Sat Aug  5 11:21:24 UTC 2023

Modified Files:
        src/sys/kern: kern_entropy.c subr_cprng.c

Log Message:
cprng(9): Drop and retake percpu reference across entropy_extract.

entropy_extract may sleep on an adaptive lock, which invalidates
percpu(9) references.

Add a note in the comment over entropy_extract about this.

Discovered by stumbling upon this panic during a test run:

[   1.0200050] panic: kernel diagnostic assertion "(cprng == 
percpu_getref(cprng_fast_percpu)) && (percpu_putref(cprng_fast_percpu), true)" 
failed: file 
"/home/riastradh/netbsd/current/src/sys/rump/librump/rumpkern/../../../crypto/cprng_fast/cprng_fast.c",
 line 117

XXX pullup-10


To generate a diff of this commit:
cvs rdiff -u -r1.64 -r1.65 src/sys/kern/kern_entropy.c
cvs rdiff -u -r1.43 -r1.44 src/sys/kern/subr_cprng.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/kern/kern_entropy.c
diff -u src/sys/kern/kern_entropy.c:1.64 src/sys/kern/kern_entropy.c:1.65
--- src/sys/kern/kern_entropy.c:1.64	Fri Aug  4 16:02:01 2023
+++ src/sys/kern/kern_entropy.c	Sat Aug  5 11:21:24 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: kern_entropy.c,v 1.64 2023/08/04 16:02:01 riastradh Exp $	*/
+/*	$NetBSD: kern_entropy.c,v 1.65 2023/08/05 11:21:24 riastradh Exp $	*/
 
 /*-
  * Copyright (c) 2019 The NetBSD Foundation, Inc.
@@ -77,7 +77,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: kern_entropy.c,v 1.64 2023/08/04 16:02:01 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: kern_entropy.c,v 1.65 2023/08/05 11:21:24 riastradh Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -1468,8 +1468,9 @@ sysctl_entropy_gather(SYSCTLFN_ARGS)
  *		EINTR/ERESTART	No entropy, ENTROPY_SIG set, and interrupted.
  *
  *	If ENTROPY_WAIT is set, allowed only in thread context.  If
- *	ENTROPY_WAIT is not set, allowed also in softint context.
- *	Forbidden in hard interrupt context.
+ *	ENTROPY_WAIT is not set, allowed also in softint context -- may
+ *	sleep on an adaptive lock up to IPL_SOFTSERIAL.  Forbidden in
+ *	hard interrupt context.
  */
 int
 entropy_extract(void *buf, size_t len, int flags)

Index: src/sys/kern/subr_cprng.c
diff -u src/sys/kern/subr_cprng.c:1.43 src/sys/kern/subr_cprng.c:1.44
--- src/sys/kern/subr_cprng.c:1.43	Fri May 13 09:40:25 2022
+++ src/sys/kern/subr_cprng.c	Sat Aug  5 11:21:24 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: subr_cprng.c,v 1.43 2022/05/13 09:40:25 riastradh Exp $	*/
+/*	$NetBSD: subr_cprng.c,v 1.44 2023/08/05 11:21:24 riastradh Exp $	*/
 
 /*-
  * Copyright (c) 2019 The NetBSD Foundation, Inc.
@@ -52,7 +52,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: subr_cprng.c,v 1.43 2022/05/13 09:40:25 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: subr_cprng.c,v 1.44 2023/08/05 11:21:24 riastradh Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -265,10 +265,39 @@ cprng_fini_cpu(void *ptr, void *cookie, 
 	kmem_free(cc->cc_drbg, sizeof(*cc->cc_drbg));
 }
 
+static void
+cprng_strong_reseed(struct cprng_strong *cprng, unsigned epoch,
+    struct cprng_cpu **ccp, int *sp)
+{
+	uint8_t seed[NIST_HASH_DRBG_SEEDLEN_BYTES];
+
+	/*
+	 * Drop everything to extract a fresh seed from the entropy
+	 * pool.  entropy_extract may sleep on an adaptive lock, which
+	 * invalidates our percpu(9) reference.
+	 *
+	 * This may race with reseeding in another thread, which is no
+	 * big deal -- worst case, we rewind the entropy epoch here and
+	 * cause the next caller to reseed again, and in the end we
+	 * just reseed a couple more times than necessary.
+	 */
+	splx(*sp);
+	percpu_putref(cprng->cs_percpu);
+	entropy_extract(seed, sizeof seed, 0);
+	*ccp = percpu_getref(cprng->cs_percpu);
+	*sp = splraiseipl(cprng->cs_iplcookie);
+
+	(*ccp)->cc_evcnt->reseed.ev_count++;
+	if (__predict_false(nist_hash_drbg_reseed((*ccp)->cc_drbg,
+		    seed, sizeof seed, NULL, 0)))
+		panic("nist_hash_drbg_reseed");
+	explicit_memset(seed, 0, sizeof seed);
+	(*ccp)->cc_epoch = epoch;
+}
+
 size_t
 cprng_strong(struct cprng_strong *cprng, void *buf, size_t len, int flags)
 {
-	uint8_t seed[NIST_HASH_DRBG_SEEDLEN_BYTES];
 	struct cprng_cpu *cc;
 	unsigned epoch;
 	int s;
@@ -293,25 +322,13 @@ cprng_strong(struct cprng_strong *cprng,
 
 	/* If the entropy epoch has changed, (re)seed.  */
 	epoch = entropy_epoch();
-	if (__predict_false(epoch != cc->cc_epoch)) {
-		entropy_extract(seed, sizeof seed, 0);
-		cc->cc_evcnt->reseed.ev_count++;
-		if (__predict_false(nist_hash_drbg_reseed(cc->cc_drbg,
-			    seed, sizeof seed, NULL, 0)))
-			panic("nist_hash_drbg_reseed");
-		explicit_memset(seed, 0, sizeof seed);
-		cc->cc_epoch = epoch;
-	}
+	if (__predict_false(epoch != cc->cc_epoch))
+		cprng_strong_reseed(cprng, epoch, &cc, &s);
 
 	/* Generate data.  Failure here means it's time to reseed.  */
 	if (__predict_false(nist_hash_drbg_generate(cc->cc_drbg, buf, len,
 		    NULL, 0))) {
-		entropy_extract(seed, sizeof seed, 0);
-		cc->cc_evcnt->reseed.ev_count++;
-		if (__predict_false(nist_hash_drbg_reseed(cc->cc_drbg,
-			    seed, sizeof seed, NULL, 0)))
-			panic("nist_hash_drbg_reseed");
-		explicit_memset(seed, 0, sizeof seed);
+		cprng_strong_reseed(cprng, epoch, &cc, &s);
 		if (__predict_false(nist_hash_drbg_generate(cc->cc_drbg,
 			    buf, len, NULL, 0)))
 			panic("nist_hash_drbg_generate");

Reply via email to