Commit 294eef2b authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] random: accounting and sleeping fixes

From: Oliver Xymoron <oxymoron@waste.org>

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.
parent fbf6dda7
...@@ -269,9 +269,9 @@ ...@@ -269,9 +269,9 @@
/* /*
* The minimum number of bits of entropy before we wake up a read on * 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 * If the entropy count falls under this number of bits, then we
...@@ -483,7 +483,6 @@ struct entropy_store { ...@@ -483,7 +483,6 @@ struct entropy_store {
unsigned add_ptr; unsigned add_ptr;
int entropy_count; int entropy_count;
int input_rotate; int input_rotate;
int extract_count;
struct poolinfo poolinfo; struct poolinfo poolinfo;
__u32 *pool; __u32 *pool;
spinlock_t lock; spinlock_t lock;
...@@ -536,7 +535,6 @@ static void clear_entropy_store(struct entropy_store *r) ...@@ -536,7 +535,6 @@ static void clear_entropy_store(struct entropy_store *r)
r->add_ptr = 0; r->add_ptr = 0;
r->entropy_count = 0; r->entropy_count = 0;
r->input_rotate = 0; r->input_rotate = 0;
r->extract_count = 0;
memset(r->pool, 0, r->poolinfo.POOLBYTES); memset(r->pool, 0, r->poolinfo.POOLBYTES);
} }
#ifdef CONFIG_SYSCTL #ifdef CONFIG_SYSCTL
...@@ -616,10 +614,12 @@ static void credit_entropy_store(struct entropy_store *r, int nbits) ...@@ -616,10 +614,12 @@ static void credit_entropy_store(struct entropy_store *r, int nbits)
} else { } else {
r->entropy_count += nbits; r->entropy_count += nbits;
if (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 == sec_random_state ? "secondary" :
r == random_state ? "primary" : "unknown", r == random_state ? "primary" : "unknown");
nbits, r->entropy_count);
} }
spin_unlock_irqrestore(&r->lock, flags); spin_unlock_irqrestore(&r->lock, flags);
...@@ -1255,6 +1255,7 @@ static void MD5Transform(__u32 buf[HASH_BUFFER_SIZE], __u32 const in[16]) ...@@ -1255,6 +1255,7 @@ static void MD5Transform(__u32 buf[HASH_BUFFER_SIZE], __u32 const in[16])
#define EXTRACT_ENTROPY_USER 1 #define EXTRACT_ENTROPY_USER 1
#define EXTRACT_ENTROPY_SECONDARY 2 #define EXTRACT_ENTROPY_SECONDARY 2
#define EXTRACT_ENTROPY_LIMIT 4
#define TMP_BUF_SIZE (HASH_BUFFER_SIZE + HASH_EXTRA_SIZE) #define TMP_BUF_SIZE (HASH_BUFFER_SIZE + HASH_EXTRA_SIZE)
#define SEC_XFER_SIZE (TMP_BUF_SIZE*4) #define SEC_XFER_SIZE (TMP_BUF_SIZE*4)
...@@ -1263,36 +1264,28 @@ static ssize_t extract_entropy(struct entropy_store *r, void * buf, ...@@ -1263,36 +1264,28 @@ static ssize_t extract_entropy(struct entropy_store *r, void * buf,
/* /*
* This utility inline function is responsible for transfering entropy * This utility inline function is responsible for transfering entropy
* from the primary pool to the secondary extraction pool. We pull * from the primary pool to the secondary extraction pool. We make
* randomness under two conditions; one is if there isn't enough entropy * sure we pull enough for a 'catastrophic reseed'.
* in the secondary pool. The other is after we have extracted 1024 bytes,
* at which point we do a "catastrophic reseeding".
*/ */
static inline void xfer_secondary_pool(struct entropy_store *r, static inline void xfer_secondary_pool(struct entropy_store *r,
size_t nbytes, __u32 *tmp) size_t nbytes, __u32 *tmp)
{ {
if (r->entropy_count < nbytes * 8 && if (r->entropy_count < nbytes * 8 &&
r->entropy_count < r->poolinfo.POOLBITS) { r->entropy_count < r->poolinfo.POOLBITS) {
int nwords = min_t(int, int bytes = max_t(int, random_read_wakeup_thresh / 8,
r->poolinfo.poolwords - r->entropy_count/32, min_t(int, nbytes, TMP_BUF_SIZE));
sizeof(tmp) / 4);
DEBUG_ENT("xfer %d from primary to %s (have %d, need %d)\n", DEBUG_ENT("%04d %04d : going to reseed %s with %d bits "
nwords * 32, "(%d of %d requested)\n",
random_state->entropy_count,
sec_random_state->entropy_count,
r == sec_random_state ? "secondary" : "unknown", 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); bytes=extract_entropy(random_state, tmp, bytes,
add_entropy_words(r, tmp, nwords); EXTRACT_ENTROPY_LIMIT);
credit_entropy_store(r, nwords * 32); add_entropy_words(r, tmp, bytes);
} credit_entropy_store(r, bytes*8);
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;
} }
} }
...@@ -1317,7 +1310,6 @@ static ssize_t extract_entropy(struct entropy_store *r, void * buf, ...@@ -1317,7 +1310,6 @@ static ssize_t extract_entropy(struct entropy_store *r, void * buf,
__u32 x; __u32 x;
unsigned long cpuflags; unsigned long cpuflags;
add_timer_randomness(&extract_timer_state, nbytes);
/* Redundant, but just in case... */ /* Redundant, but just in case... */
if (r->entropy_count > r->poolinfo.POOLBITS) if (r->entropy_count > r->poolinfo.POOLBITS)
...@@ -1329,10 +1321,15 @@ static ssize_t extract_entropy(struct entropy_store *r, void * buf, ...@@ -1329,10 +1321,15 @@ static ssize_t extract_entropy(struct entropy_store *r, void * buf,
/* Hold lock while accounting */ /* Hold lock while accounting */
spin_lock_irqsave(&r->lock, cpuflags); 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 == sec_random_state ? "secondary" :
r == random_state ? "primary" : "unknown", r == random_state ? "primary" : "unknown");
r->entropy_count, nbytes * 8);
if (flags & EXTRACT_ENTROPY_LIMIT && nbytes >= r->entropy_count / 8)
nbytes = r->entropy_count / 8;
if (r->entropy_count / 8 >= nbytes) if (r->entropy_count / 8 >= nbytes)
r->entropy_count -= nbytes*8; r->entropy_count -= nbytes*8;
...@@ -1342,10 +1339,16 @@ static ssize_t extract_entropy(struct entropy_store *r, void * buf, ...@@ -1342,10 +1339,16 @@ static ssize_t extract_entropy(struct entropy_store *r, void * buf,
if (r->entropy_count < random_write_wakeup_thresh) if (r->entropy_count < random_write_wakeup_thresh)
wake_up_interruptible(&random_write_wait); 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); spin_unlock_irqrestore(&r->lock, cpuflags);
ret = 0; ret = 0;
while (nbytes) { while (nbytes) {
/* /*
...@@ -1357,7 +1360,16 @@ static ssize_t extract_entropy(struct entropy_store *r, void * buf, ...@@ -1357,7 +1360,16 @@ static ssize_t extract_entropy(struct entropy_store *r, void * buf,
ret = -ERESTARTSYS; ret = -ERESTARTSYS;
break; break;
} }
DEBUG_ENT("%04d %04d : extract feeling sleepy (%d bytes left)\n",
random_state->entropy_count,
sec_random_state->entropy_count, nbytes);
schedule(); 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 */ /* Hash the pool to get the output */
...@@ -1406,7 +1418,6 @@ static ssize_t extract_entropy(struct entropy_store *r, void * buf, ...@@ -1406,7 +1418,6 @@ static ssize_t extract_entropy(struct entropy_store *r, void * buf,
nbytes -= i; nbytes -= i;
buf += i; buf += i;
ret += i; ret += i;
add_timer_randomness(&extract_timer_state, nbytes);
} }
/* Wipe data just returned from memory */ /* Wipe data just returned from memory */
...@@ -1533,15 +1544,27 @@ random_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos) ...@@ -1533,15 +1544,27 @@ random_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos)
if (nbytes == 0) if (nbytes == 0)
return 0; return 0;
add_wait_queue(&random_read_wait, &wait);
while (nbytes > 0) { while (nbytes > 0) {
set_current_state(TASK_INTERRUPTIBLE);
n = nbytes; n = nbytes;
if (n > SEC_XFER_SIZE) if (n > SEC_XFER_SIZE)
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 (n == 0) {
if (file->f_flags & O_NONBLOCK) { if (file->f_flags & O_NONBLOCK) {
retval = -EAGAIN; retval = -EAGAIN;
...@@ -1551,12 +1574,27 @@ random_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos) ...@@ -1551,12 +1574,27 @@ random_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos)
retval = -ERESTARTSYS; retval = -ERESTARTSYS;
break; 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; continue;
} }
n = extract_entropy(sec_random_state, buf, n,
EXTRACT_ENTROPY_USER |
EXTRACT_ENTROPY_SECONDARY);
if (n < 0) { if (n < 0) {
retval = n; retval = n;
break; break;
...@@ -1567,8 +1605,6 @@ random_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos) ...@@ -1567,8 +1605,6 @@ random_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos)
break; /* This break makes the device work */ break; /* This break makes the device work */
/* like a named pipe */ /* 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. * If we gave the user some bytes, update the access time.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment