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
This commit is contained in:
riastradh 2023-08-05 11:21:24 +00:00
parent 8d2e8478af
commit 763d441de3
2 changed files with 40 additions and 22 deletions

View File

@ -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)

View File

@ -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, struct cpu_info *ci)
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, void *buf, size_t len, int flags)
/* 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");