tech-kern archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
More eyes requested (Re: Kernel entropy pool / cprng race fix)
On Fri, Apr 06, 2012 at 10:59:16AM -0400, Thor Lancelot Simon wrote:
> The attached patch should fix a race that exists when an entropy sink
> (such as a CPRNG instance that is being reseeded) is destroyed while
> it is currently being reseeded.
>
> I've minimally tested it but since the problem is hard to reproduce
> I'd appreciate additional testers or additional eyes.
>
> The diff is also at http://www.panix.com/~tls/seed-destroy.diff .
This is checked in, and has one bugfix courtesy of Matthias Drochner.
The attached patch is -- mostly -- an optimization for the odd case
where someone runs a program very frequently that opens /dev/urandom
and does a short read every time it's run (some versions of the perl
interpreter, invoked, let's say, on a busy CGI host, act like this).
It adds a per-CPU persistent cprng_strong that is used to service short
requests. I don't use percpu(9) as I don't really care if occasionally
we switch away from the "home" CPU of a cprng before generating; there
is locking to make that safe. This way avoids a lot of kpreempt_disable()s.
It also changes the rekeying strategy on pool full. In the course of
testing that, I discovered a bug in the reseed / destroy race fix:
the reseed_pending variable on the cprng could get set but not cleared
if a reseed was skipped because the cprng's mutex was held.
The patch attached here tries to fix that, but now there is a LOCKDEBUG
problem: sometimes we seem to get to the uiomove() in rnd_read with
the reseed spin mutex held. I don't see how this is possible but it
can be reproduced like this:
% sysctl -w ddb.onpanic=1
% dd if=/dev/urandom of=/dev/null bs=4 &
% dd if=/dev/sd0d of=/dev/null &
When the entropy pool fills and the per-cpu rng is rekeyed, the
LOCKDEBUG panic occurs.
I'd really appreciate help with this; I've just spent my whole
commute on it and I don't see how we get to that uiomove() with a
spin lock held! The rekeying fix and short-read optimizations are
important, since they address real problems with real-world use cases,
so I really want to get this debugged and in the tree.
Diff is also at http://www.panix.com/~tls/rngpercpu.diff .
Thor
? 1
? rh
? rngpercpu.diff
? seed-destroy.diff
? arch/amd64/conf/RNDVERBOSE
? arch/arm/xscale/.iopaau.c.swp
? dev/.rndpseudo.c.swp
? kern/1
? kern/lastbatch.txt
? sys/.cprng.h.swp
? sys/.cpu.h.swp
Index: dev/rndpseudo.c
===================================================================
RCS file: /cvsroot/src/sys/dev/rndpseudo.c,v
retrieving revision 1.6
diff -r1.6 rndpseudo.c
56a57
> #include <sys/cpu.h>
98a100,104
> * The per-CPU RNGs used for short requests
> */
> cprng_strong_t **rp_cpurngs;
>
> /*
195a202,204
>
> rp_cpurngs = kmem_zalloc(maxcpus * sizeof(cprng_strong_t *),
> KM_SLEEP);
254a264
> cprng_strong_t *cprng;
256a267
> struct cpu_info *ci = curcpu();
265,268c276,278
< if (ctx->cprng == NULL) {
< rnd_alloc_cprng(ctx);
< if (__predict_false(ctx->cprng == NULL)) {
< return EIO;
---
> if (ctx->hard || uio->uio_resid > NIST_BLOCK_KEYLEN_BYTES) {
> if (ctx->cprng == NULL) {
> rnd_alloc_cprng(ctx);
269a280,299
> cprng = ctx->cprng;
> } else {
> int index = cpu_index(ci);
>
> if (__predict_false(rp_cpurngs[index] == NULL)) {
> char rngname[32];
>
> snprintf(rngname, sizeof(rngname),
> "%s-short", cpu_name(ci));
> rp_cpurngs[index] =
> cprng_strong_create(rngname, IPL_NONE,
> CPRNG_INIT_ANY |
> CPRNG_REKEY_ANY);
> }
> cprng = rp_cpurngs[index];
> }
>
> if (__predict_false(cprng == NULL)) {
> printf("NULL rng!\n");
> return EIO;
272c302
< strength = cprng_strong_strength(ctx->cprng);
---
> strength = cprng_strong_strength(cprng);
288c318
< nread = cprng_strong(ctx->cprng, bf, n,
---
> nread = cprng_strong(cprng, bf, n,
306c336
< cprng_strong_deplete(ctx->cprng);
---
> cprng_strong_deplete(cprng);
308a339
>
Index: kern/kern_rndpool.c
===================================================================
RCS file: /cvsroot/src/sys/kern/kern_rndpool.c,v
retrieving revision 1.1
diff -r1.1 kern_rndpool.c
55c55,56
< int rnd_full;
---
> int rnd_full = 0; /* Flag: is the pool full? */
> int rnd_filled = 0; /* Count: how many times filled? */
218a220
> rnd_filled++;
249c251,253
< rnd_full = 0;
---
> if (rp->stats.curentropy < RND_POOLBITS / 2) {
> rnd_full = 0;
> }
Index: kern/subr_cprng.c
===================================================================
RCS file: /cvsroot/src/sys/kern/subr_cprng.c,v
retrieving revision 1.7
diff -r1.7 subr_cprng.c
78,79c78,79
< if (!(c->reseed_pending) && mutex_tryenter(&c->reseed.mtx)) {
< c->reseed_pending = 1;
---
> if (mutex_tryenter(&c->reseed.mtx) && !c->reseed.pending) {
> c->reseed.pending = 1;
92a93,94
> KASSERT(mutex_owned(&c->reseed.mtx));
>
105c107,108
< c->reseed_pending = 0;
---
> c->entropy_serial = rnd_filled;
> c->reseed.pending = 0;
127c130
< c->reseed_pending = 0;
---
> c->reseed.pending = 0;
129a133
> c->entropy_serial = rnd_filled;
256,260c260,261
< }
<
< if (rnd_full) {
< if (!c->rekeyed_on_full) {
< c->rekeyed_on_full++;
---
> } else if (rnd_full) {
> if (c->entropy_serial != rnd_filled) {
263,264d263
< } else {
< c->rekeyed_on_full = 0;
283c282
< if (c->reseed_pending) {
---
> if (c->reseed.pending) {
Index: sys/cprng.h
===================================================================
RCS file: /cvsroot/src/sys/sys/cprng.h,v
retrieving revision 1.4
diff -r1.4 cprng.h
88c88
< int rekeyed_on_full;
---
> int entropy_serial;
Index: sys/rnd.h
===================================================================
RCS file: /cvsroot/src/sys/sys/rnd.h,v
retrieving revision 1.30
diff -r1.30 rnd.h
132a133
> int pending; /* currently in use */
180a182
> extern int rnd_filled;
Home |
Main Index |
Thread Index |
Old Index