diff --git a/sys/dev/rndpseudo.c b/sys/dev/rndpseudo.c index 2743381691b9..b035eda0fb8b 100644 --- a/sys/dev/rndpseudo.c +++ b/sys/dev/rndpseudo.c @@ -1,4 +1,4 @@ -/* $NetBSD: rndpseudo.c,v 1.9 2012/04/20 21:57:33 tls Exp $ */ +/* $NetBSD: rndpseudo.c,v 1.10 2012/05/19 16:00:41 tls Exp $ */ /*- * Copyright (c) 1997-2011 The NetBSD Foundation, Inc. @@ -30,7 +30,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: rndpseudo.c,v 1.9 2012/04/20 21:57:33 tls Exp $"); +__KERNEL_RCSID(0, "$NetBSD: rndpseudo.c,v 1.10 2012/05/19 16:00:41 tls Exp $"); #if defined(_KERNEL_OPT) #include "opt_compat_netbsd.h" @@ -309,14 +309,23 @@ rnd_read(struct file * fp, off_t *offp, struct uio *uio, /* XXX is this _really_ what's wanted? */ if (ctx->hard) { n = MIN(want, strength - ctx->bytesonkey); - ctx->bytesonkey += n; + if (n < 1) { + cprng_strong_deplete(cprng); + n = MIN(want, strength); + ctx->bytesonkey = 0; + membar_producer(); + } } else { n = want; } nread = cprng_strong(cprng, bf, n, (fp->f_flag & FNONBLOCK) ? FNONBLOCK : 0); - if (nread != n) { + + if (ctx->hard && nread > 0) { + atomic_add_int(&ctx->bytesonkey, nread); + } + if (nread < 1) { if (fp->f_flag & FNONBLOCK) { ret = EWOULDBLOCK; } else { @@ -331,12 +340,6 @@ rnd_read(struct file * fp, off_t *offp, struct uio *uio, } } out: - if (ctx->bytesonkey >= strength) { - /* Force reseed of underlying DRBG (prediction resistance) */ - cprng_strong_deplete(cprng); - ctx->bytesonkey = 0; - } - pool_cache_put(rp_pc, bf); return (ret); } diff --git a/sys/kern/subr_cprng.c b/sys/kern/subr_cprng.c index 02dddf0623e9..b3cafdfe64e1 100644 --- a/sys/kern/subr_cprng.c +++ b/sys/kern/subr_cprng.c @@ -1,4 +1,4 @@ -/* $NetBSD: subr_cprng.c,v 1.8 2012/04/17 02:50:39 tls Exp $ */ +/* $NetBSD: subr_cprng.c,v 1.9 2012/05/19 16:00:41 tls Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -46,7 +46,7 @@ #include -__KERNEL_RCSID(0, "$NetBSD: subr_cprng.c,v 1.8 2012/04/17 02:50:39 tls Exp $"); +__KERNEL_RCSID(0, "$NetBSD: subr_cprng.c,v 1.9 2012/05/19 16:00:41 tls Exp $"); void cprng_init(void) @@ -144,6 +144,9 @@ cprng_strong_reseed(void *const arg) #ifdef RND_VERBOSE printf("cprng: sink %s cprng busy, no reseed\n", c->reseed.name); #endif + if (c->flags & CPRNG_USE_CV) { /* XXX if flags change? */ + cv_broadcast(&c->cv); + } return; } @@ -240,23 +243,32 @@ cprng_strong(cprng_strong_t *const c, void *const p, size_t len, int flags) "failed.", c->name); } } else { - if (!(flags & FNONBLOCK) && - (c->flags & CPRNG_USE_CV)) { - int wr; + int wr; + do { cprng_strong_sched_reseed(c); - do { - wr = cv_wait_sig(&c->cv, &c->mtx); - if (wr == ERESTART) { - mutex_exit(&c->mtx); - return 0; - } - } while (nist_ctr_drbg_generate(&c->drbg, p, - len, &cc, - sizeof(cc))); - } else { - len = 0; - } + if ((flags & FNONBLOCK) || + !(c->flags & CPRNG_USE_CV)) { + len = 0; + break; + } + /* + * XXX There's a race with the cv_broadcast + * XXX in cprng_strong_sched_reseed, because + * XXX of the use of tryenter in that function. + * XXX This "timedwait" hack works around it, + * XXX at the expense of occasionaly polling + * XXX for success on a /dev/random rekey. + */ + wr = cv_timedwait_sig(&c->cv, &c->mtx, + mstohz(100)); + if (wr == ERESTART) { + mutex_exit(&c->mtx); + return 0; + } + } while (nist_ctr_drbg_generate(&c->drbg, p, + len, &cc, + sizeof(cc))); } }