From 17cdcc57fab1a27229751d2ddde59ee1f3749af8 Mon Sep 17 00:00:00 2001 From: John Denker Date: Mon, 7 Oct 2013 20:23:25 -0700 Subject: first attempt to make urandom less voracious; also add scheme for looking at critical variables --- drivers/char/random.c | 124 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 113 insertions(+), 11 deletions(-) diff --git a/drivers/char/random.c b/drivers/char/random.c index 0d91fe5..e526d63 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -277,13 +277,28 @@ #define SEC_XFER_SIZE 512 #define EXTRACT_SIZE 10 +/* Choose 160 bits. Seems reasonable. Recommended in the Yarrow paper. */ +#define RESEED_BATCH 160 /* bits */ + +/* + * The nonblocking output pool will not drag the input pool below this + * fill fraction: + */ +#define FILL_FRAC(X) ((X)*3/4) + #define LONGS(x) (((x) + sizeof(unsigned long) - 1)/sizeof(unsigned long)) +#define W2BYTE(X) ((X)*4) /* convert #words to #bytes */ +#define W2BIT(X) ((X)*32) /* convert #words to #bits */ +#define BIT2BYTE(X) ((X)/8) /* convert #bits to #bytes */ +#define BYTE2BIT(X) ((X)*8) /* convert #bytes to #bits */ /* * The minimum number of bits of entropy before we wake up a read on - * /dev/random. Should be enough to do a significant reseed. + * /dev/random. + * Can be changed at runtime via /proc/. + * The minimum value is RESEED_BATCH; enforced at runtime. */ -static int random_read_wakeup_thresh = 64; +static int random_read_wakeup_thresh = RESEED_BATCH; /* * If the entropy count falls under this number of bits, then we @@ -431,7 +446,9 @@ struct entropy_store { unsigned add_ptr; unsigned input_rotate; int entropy_count; - int entropy_total; + int entropy_total; /* entropy input; used only during initialization */ + uint64_t extracted_subttl; + uint64_t extracted_total; unsigned int initialized:1; bool last_data_init; __u8 last_data[EXTRACT_SIZE]; @@ -440,6 +457,11 @@ struct entropy_store { static __u32 input_pool_data[INPUT_POOL_WORDS]; static __u32 blocking_pool_data[OUTPUT_POOL_WORDS]; static __u32 nonblocking_pool_data[OUTPUT_POOL_WORDS]; +#ifdef NOTUSED + static uint64_t uint64_zero = 0; + static uint64_t uint64_max = ~((uint64_t)0); +#endif +static uint64_t play = sizeof(uint64_t)*10000 + sizeof(ulong)*100 + sizeof(int); static struct entropy_store input_pool = { .poolinfo = &poolinfo_table[0], @@ -816,12 +838,12 @@ static void xfer_secondary_pool(struct entropy_store *r, size_t nbytes) if (r->pull && r->entropy_count < nbytes * 8 && r->entropy_count < r->poolinfo->POOLBITS) { - /* If we're limited, always leave two wakeup worth's BITS */ - int rsvd = r->limit ? 0 : random_read_wakeup_thresh/4; + /* If we're non-blocking, protect the reserved amount of entropy: */ + int rsvd = r->limit ? 0 : FILL_FRAC(W2BYTE(r->pull->poolinfo->poolwords)); int bytes = nbytes; - /* pull at least as many as BYTES as wakeup BITS */ - bytes = max_t(int, bytes, random_read_wakeup_thresh / 8); + /* Request a big-enough batch: */ + bytes = max_t(int, bytes, BIT2BYTE(RESEED_BATCH)); /* but never more than the buffer size */ bytes = min_t(int, bytes, sizeof(tmp)); @@ -829,8 +851,7 @@ static void xfer_secondary_pool(struct entropy_store *r, size_t nbytes) "(%zu of %d requested)\n", r->name, bytes * 8, nbytes * 8, r->entropy_count); - bytes = extract_entropy(r->pull, tmp, bytes, - random_read_wakeup_thresh / 8, rsvd); + bytes = extract_entropy(r->pull, tmp, bytes, BIT2BYTE(RESEED_BATCH), rsvd); mix_pool_bytes(r, tmp, bytes, NULL); credit_entropy_bits(r, bytes*8); } @@ -1007,6 +1028,10 @@ static ssize_t extract_entropy(struct entropy_store *r, void *buf, /* Wipe data just returned from memory */ memset(tmp, 0, sizeof(tmp)); + if (ret > 0) { + r->extracted_subttl += BYTE2BIT(ret); + r->extracted_total += BYTE2BIT(ret); + } return ret; } @@ -1045,6 +1070,10 @@ static ssize_t extract_entropy_user(struct entropy_store *r, void __user *buf, /* Wipe data just returned from memory */ memset(tmp, 0, sizeof(tmp)); + if (ret > 0) { + r->extracted_subttl += BYTE2BIT(ret); + r->extracted_total += BYTE2BIT(ret); + } return ret; } @@ -1081,7 +1110,7 @@ void get_random_bytes_arch(void *buf, int nbytes) if (!arch_get_random_long(&v)) break; - + memcpy(p, &v, chunk); p += chunk; nbytes -= chunk; @@ -1110,6 +1139,8 @@ static void init_std_data(struct entropy_store *r) r->entropy_count = 0; r->entropy_total = 0; + r->extracted_subttl = 0; + r->extracted_total = 0; r->last_data_init = false; mix_pool_bytes(r, &now, sizeof(now), NULL); for (i = r->poolinfo->POOLBYTES; i > 0; i -= sizeof(rv)) { @@ -1367,7 +1398,8 @@ EXPORT_SYMBOL(generate_random_uuid); #include -static int min_read_thresh = 8, min_write_thresh; +static int min_read_thresh = RESEED_BATCH; +static int min_write_thresh; /* shouldn't this have a value? */ static int max_read_thresh = INPUT_POOL_WORDS * 32; static int max_write_thresh = INPUT_POOL_WORDS * 32; static char sysctl_bootid[16]; @@ -1425,6 +1457,76 @@ struct ctl_table random_table[] = { .proc_handler = proc_dointvec, .data = &input_pool.entropy_count, }, + { + .procname = "entropy_avail_r", + .maxlen = sizeof(int), + .mode = 0444, + .proc_handler = proc_dointvec, + .data = &blocking_pool.entropy_count, + }, + { + .procname = "entropy_avail_ur", + .maxlen = sizeof(int), + .mode = 0444, + .proc_handler = proc_dointvec, + .data = &nonblocking_pool.entropy_count, + }, + { + .procname = "play_int", + .maxlen = sizeof(uint64_t), + .mode = 0644, + .proc_handler = proc_dointvec, + .data = &play, + }, + { + .procname = "play_long", + .maxlen = sizeof(uint64_t), + .mode = 0644, + .proc_handler = proc_doulongvec_minmax, + .data = &play, + }, + { + .procname = "extracted_total", + .maxlen = sizeof(uint64_t), + .mode = 0644, + .proc_handler = proc_doulongvec_minmax, + .data = &input_pool.extracted_total, + }, + { + .procname = "extracted_total_r", + .maxlen = sizeof(uint64_t), + .mode = 0644, + .proc_handler = proc_doulongvec_minmax, + .data = &blocking_pool.extracted_total, + }, + { + .procname = "extracted_total_ur", + .maxlen = sizeof(uint64_t), + .mode = 0644, + .proc_handler = proc_doulongvec_minmax, + .data = &nonblocking_pool.extracted_total, + }, + { + .procname = "extracted_subttl", + .maxlen = sizeof(uint64_t), + .mode = 0644, + .proc_handler = proc_doulongvec_minmax, + .data = &input_pool.extracted_subttl, + }, + { + .procname = "extracted_subttl_r", + .maxlen = sizeof(uint64_t), + .mode = 0644, + .proc_handler = proc_doulongvec_minmax, + .data = &blocking_pool.extracted_subttl, + }, + { + .procname = "extracted_subttl_ur", + .maxlen = sizeof(uint64_t), + .mode = 0644, + .proc_handler = proc_doulongvec_minmax, + .data = &nonblocking_pool.extracted_subttl, + }, { .procname = "read_wakeup_threshold", .data = &random_read_wakeup_thresh, -- cgit v1.2.3