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