From: Oliver Xymoron This fixes several calculation errors and races in entropy accounting that would allow /dev/random output to greatly exceed the measured entropy collection. This doesn't include any of my more controversial hardening, it just makes it behave as intended. It also corrects the operation of the 'catastrophic reseeding' feature so that it will actually prevent the state extension attack it's meant to guard against. And finally, it also fixes a couple missed wake-up and accidental sleep bugs uncovered by the above fixes. Debug instrumentation has been improved to help verify correctness. drivers/char/random.c | 128 ++++++++++++++++++++++++++++++++------------------ 1 files changed, 82 insertions(+), 46 deletions(-) diff -puN drivers/char/random.c~random-accounting-and-sleeping-fixes drivers/char/random.c --- 25/drivers/char/random.c~random-accounting-and-sleeping-fixes 2003-08-02 15:20:43.000000000 -0700 +++ 25-akpm/drivers/char/random.c 2003-08-02 15:20:43.000000000 -0700 @@ -269,9 +269,9 @@ /* * The minimum number of bits of entropy before we wake up a read on - * /dev/random. Should always be at least 8, or at least 1 byte. + * /dev/random. Should be enough to do a significant reseed. */ -static int random_read_wakeup_thresh = 8; +static int random_read_wakeup_thresh = 64; /* * If the entropy count falls under this number of bits, then we @@ -483,7 +483,6 @@ struct entropy_store { unsigned add_ptr; int entropy_count; int input_rotate; - int extract_count; struct poolinfo poolinfo; __u32 *pool; spinlock_t lock; @@ -536,7 +535,6 @@ static void clear_entropy_store(struct e r->add_ptr = 0; r->entropy_count = 0; r->input_rotate = 0; - r->extract_count = 0; memset(r->pool, 0, r->poolinfo.POOLBYTES); } #ifdef CONFIG_SYSCTL @@ -616,10 +614,12 @@ static void credit_entropy_store(struct } else { r->entropy_count += nbits; if (nbits) - DEBUG_ENT("%s added %d bits, now %d\n", + DEBUG_ENT("%04d %04d : added %d bits to %s\n", + random_state->entropy_count, + sec_random_state->entropy_count, + nbits, r == sec_random_state ? "secondary" : - r == random_state ? "primary" : "unknown", - nbits, r->entropy_count); + r == random_state ? "primary" : "unknown"); } spin_unlock_irqrestore(&r->lock, flags); @@ -1255,6 +1255,7 @@ static void MD5Transform(__u32 buf[HASH_ #define EXTRACT_ENTROPY_USER 1 #define EXTRACT_ENTROPY_SECONDARY 2 +#define EXTRACT_ENTROPY_LIMIT 4 #define TMP_BUF_SIZE (HASH_BUFFER_SIZE + HASH_EXTRA_SIZE) #define SEC_XFER_SIZE (TMP_BUF_SIZE*4) @@ -1263,36 +1264,28 @@ static ssize_t extract_entropy(struct en /* * This utility inline function is responsible for transfering entropy - * from the primary pool to the secondary extraction pool. We pull - * randomness under two conditions; one is if there isn't enough entropy - * in the secondary pool. The other is after we have extracted 1024 bytes, - * at which point we do a "catastrophic reseeding". + * from the primary pool to the secondary extraction pool. We make + * sure we pull enough for a 'catastrophic reseed'. */ static inline void xfer_secondary_pool(struct entropy_store *r, size_t nbytes, __u32 *tmp) { if (r->entropy_count < nbytes * 8 && r->entropy_count < r->poolinfo.POOLBITS) { - int nwords = min_t(int, - r->poolinfo.poolwords - r->entropy_count/32, - sizeof(tmp) / 4); + int bytes = max_t(int, random_read_wakeup_thresh / 8, + min_t(int, nbytes, TMP_BUF_SIZE)); - DEBUG_ENT("xfer %d from primary to %s (have %d, need %d)\n", - nwords * 32, + DEBUG_ENT("%04d %04d : going to reseed %s with %d bits " + "(%d of %d requested)\n", + random_state->entropy_count, + sec_random_state->entropy_count, r == sec_random_state ? "secondary" : "unknown", - r->entropy_count, nbytes * 8); + bytes * 8, nbytes * 8, r->entropy_count); - extract_entropy(random_state, tmp, nwords * 4, 0); - add_entropy_words(r, tmp, nwords); - credit_entropy_store(r, nwords * 32); - } - if (r->extract_count > 1024) { - DEBUG_ENT("reseeding %s with %d from primary\n", - r == sec_random_state ? "secondary" : "unknown", - sizeof(tmp) * 8); - extract_entropy(random_state, tmp, sizeof(tmp), 0); - add_entropy_words(r, tmp, sizeof(tmp) / 4); - r->extract_count = 0; + bytes=extract_entropy(random_state, tmp, bytes, + EXTRACT_ENTROPY_LIMIT); + add_entropy_words(r, tmp, bytes); + credit_entropy_store(r, bytes*8); } } @@ -1317,7 +1310,6 @@ static ssize_t extract_entropy(struct en __u32 x; unsigned long cpuflags; - add_timer_randomness(&extract_timer_state, nbytes); /* Redundant, but just in case... */ if (r->entropy_count > r->poolinfo.POOLBITS) @@ -1329,10 +1321,15 @@ static ssize_t extract_entropy(struct en /* Hold lock while accounting */ spin_lock_irqsave(&r->lock, cpuflags); - DEBUG_ENT("%s has %d bits, want %d bits\n", + DEBUG_ENT("%04d %04d : trying to extract %d bits from %s\n", + random_state->entropy_count, + sec_random_state->entropy_count, + nbytes * 8, r == sec_random_state ? "secondary" : - r == random_state ? "primary" : "unknown", - r->entropy_count, nbytes * 8); + r == random_state ? "primary" : "unknown"); + + if (flags & EXTRACT_ENTROPY_LIMIT && nbytes >= r->entropy_count / 8) + nbytes = r->entropy_count / 8; if (r->entropy_count / 8 >= nbytes) r->entropy_count -= nbytes*8; @@ -1342,10 +1339,16 @@ static ssize_t extract_entropy(struct en if (r->entropy_count < random_write_wakeup_thresh) wake_up_interruptible(&random_write_wait); - r->extract_count += nbytes; + DEBUG_ENT("%04d %04d : debiting %d bits from %s%s\n", + random_state->entropy_count, + sec_random_state->entropy_count, + nbytes * 8, + r == sec_random_state ? "secondary" : + r == random_state ? "primary" : "unknown", + flags & EXTRACT_ENTROPY_LIMIT ? "" : " (unlimited)"); spin_unlock_irqrestore(&r->lock, cpuflags); - + ret = 0; while (nbytes) { /* @@ -1357,7 +1360,16 @@ static ssize_t extract_entropy(struct en ret = -ERESTARTSYS; break; } + + DEBUG_ENT("%04d %04d : extract feeling sleepy (%d bytes left)\n", + random_state->entropy_count, + sec_random_state->entropy_count, nbytes); + schedule(); + + DEBUG_ENT("%04d %04d : extract woke up\n", + random_state->entropy_count, + sec_random_state->entropy_count); } /* Hash the pool to get the output */ @@ -1406,7 +1418,6 @@ static ssize_t extract_entropy(struct en nbytes -= i; buf += i; ret += i; - add_timer_randomness(&extract_timer_state, nbytes); } /* Wipe data just returned from memory */ @@ -1533,15 +1544,27 @@ random_read(struct file * file, char * b if (nbytes == 0) return 0; - add_wait_queue(&random_read_wait, &wait); while (nbytes > 0) { - set_current_state(TASK_INTERRUPTIBLE); - n = nbytes; if (n > SEC_XFER_SIZE) n = SEC_XFER_SIZE; - if (n > random_state->entropy_count / 8) - n = random_state->entropy_count / 8; + + DEBUG_ENT("%04d %04d : reading %d bits, p: %d s: %d\n", + random_state->entropy_count, + sec_random_state->entropy_count, + n*8, random_state->entropy_count, + sec_random_state->entropy_count); + + n = extract_entropy(sec_random_state, buf, n, + EXTRACT_ENTROPY_USER | + EXTRACT_ENTROPY_LIMIT | + EXTRACT_ENTROPY_SECONDARY); + + DEBUG_ENT("%04d %04d : read got %d bits (%d still needed)\n", + random_state->entropy_count, + sec_random_state->entropy_count, + n*8, (nbytes-n)*8); + if (n == 0) { if (file->f_flags & O_NONBLOCK) { retval = -EAGAIN; @@ -1551,12 +1574,27 @@ random_read(struct file * file, char * b retval = -ERESTARTSYS; break; } - schedule(); + + DEBUG_ENT("%04d %04d : sleeping?\n", + random_state->entropy_count, + sec_random_state->entropy_count); + + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&random_read_wait, &wait); + + if (sec_random_state->entropy_count / 8 == 0) + schedule(); + + set_current_state(TASK_RUNNING); + remove_wait_queue(&random_read_wait, &wait); + + DEBUG_ENT("%04d %04d : waking up\n", + random_state->entropy_count, + sec_random_state->entropy_count); + continue; } - n = extract_entropy(sec_random_state, buf, n, - EXTRACT_ENTROPY_USER | - EXTRACT_ENTROPY_SECONDARY); + if (n < 0) { retval = n; break; @@ -1567,8 +1605,6 @@ random_read(struct file * file, char * b break; /* This break makes the device work */ /* like a named pipe */ } - current->state = TASK_RUNNING; - remove_wait_queue(&random_read_wait, &wait); /* * If we gave the user some bytes, update the access time. _