Reading from /dev/random may return data while the getrandom syscall is still blocking.
Those bytes are not yet cryptographically secure. The first byte from /dev/random can have as little as 8 bits entropy estimation. Once a read blocks, it will block until /proc/sys/kernel/random/read_wakeup_threshold bits are available, which is usually 64 bits, but can be configured as low as 8 bits. A select will wake up when at least read_wakeup_threshold bits are available. Also when constantly reading bytes out of /dev/random it will prevent the crng init done event forever. Fixed by making read and select on /dev/random wait until the crng is fully initialized and the blocking_pool is also initialized which means that more than 128 bits of entopy have been accumulated in the blocking_pool. Signed-off-by: Bernd Edlinger <bernd.edlin...@hotmail.de> --- The v3 version waits much longer than the v2 version, since first 128 bits from the input_pool go into the CRNG, the next 64 bits are only accounted 3/4 = 48 bits in the blocking_pool, so we need in total 192 bits from the input_pool until the blocking_pool is initialized And another 64 bits until a select on /dev/random wakes up. On a system with only interrupt_randomness, this takes 128+192+64=384 random bits, about 6.5 minutes from boot until /dev/random is readable. Maybe this is taking too long, after the CRNG ready? After the input_pool had already been initialized, I wonder if feeding the next 64 bits from the input pool to the empty blocking_pool could already be considered to be good enough to derive the first random byte from the blocking_pool? Thanks Bernd. --- drivers/char/random.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/char/random.c b/drivers/char/random.c index 38c6d1a..666102d 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -476,6 +476,7 @@ struct entropy_store { __u8 last_data[EXTRACT_SIZE]; }; +static void _xfer_secondary_pool(struct entropy_store *r, size_t nbytes); static ssize_t extract_entropy(struct entropy_store *r, void *buf, size_t nbytes, int min, int rsvd); static ssize_t _extract_entropy(struct entropy_store *r, void *buf, @@ -719,8 +720,16 @@ static void credit_entropy_bits(struct entropy_store *r, int nbits) entropy_bits = r->entropy_count >> ENTROPY_SHIFT; } + if (crng_ready() && !blocking_pool.initialized && + entropy_bits >= random_read_wakeup_bits) { + _xfer_secondary_pool(&blocking_pool, entropy_bits / 8); + r->entropy_total = 0; + entropy_bits = r->entropy_count >> ENTROPY_SHIFT; + } + /* should we wake readers? */ - if (entropy_bits >= random_read_wakeup_bits && + if (blocking_pool.initialized && + entropy_bits >= random_read_wakeup_bits && wq_has_sleeper(&random_read_wait)) { wake_up_interruptible(&random_read_wait); kill_fasync(&fasync, SIGIO, POLL_IN); @@ -1317,7 +1326,6 @@ void add_disk_randomness(struct gendisk *disk) * from the primary pool to the secondary extraction pool. We make * sure we pull enough for a 'catastrophic reseed'. */ -static void _xfer_secondary_pool(struct entropy_store *r, size_t nbytes); static void xfer_secondary_pool(struct entropy_store *r, size_t nbytes) { if (!r->pull || @@ -1661,7 +1669,7 @@ void get_random_bytes(void *buf, int nbytes) */ int wait_for_random_bytes(void) { - if (likely(crng_ready())) + if (crng_ready()) return 0; return wait_event_interruptible(crng_init_wait, crng_ready()); } @@ -1851,7 +1859,9 @@ void rand_initialize_disk(struct gendisk *disk) nbytes = min_t(size_t, nbytes, SEC_XFER_SIZE); while (1) { - n = extract_entropy_user(&blocking_pool, buf, nbytes); + n = blocking_pool.initialized + ? extract_entropy_user(&blocking_pool, buf, nbytes) + : 0; if (n < 0) return n; trace_random_read(n*8, (nbytes-n)*8, @@ -1865,6 +1875,7 @@ void rand_initialize_disk(struct gendisk *disk) return -EAGAIN; wait_event_interruptible(random_read_wait, + blocking_pool.initialized && ENTROPY_BITS(&input_pool) >= random_read_wakeup_bits); if (signal_pending(current)) @@ -1909,7 +1920,8 @@ void rand_initialize_disk(struct gendisk *disk) poll_wait(file, &random_read_wait, wait); poll_wait(file, &random_write_wait, wait); mask = 0; - if (ENTROPY_BITS(&input_pool) >= random_read_wakeup_bits) + if (blocking_pool.initialized && + ENTROPY_BITS(&input_pool) >= random_read_wakeup_bits) mask |= EPOLLIN | EPOLLRDNORM; if (ENTROPY_BITS(&input_pool) < random_write_wakeup_bits) mask |= EPOLLOUT | EPOLLWRNORM; -- 1.9.1