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