Fix some bugs in the /dev/random driver which causes getrandom(2) to

unblock earlier than designed.  Thanks to Jann Horn from Google's
 Project Zero for pointing this out to me.
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCAAdFiEEK2m5VNv+CHkogTfJ8vlZVpUNgaMFAlrcCWAACgkQ8vlZVpUN
 gaOedwf/e1OU7CXMiinCcGfsr5XydZrEivaqS9QmqAKsLzJSNDDu1Jw6N9cbWagp
 OEIIRZdaFPImZHosEbjOW12Z3nxnlDC8jtOLyLIRGSA2u4RXd03RupHhQW4cE7ys
 EOljEvK5KFDIlPa947R5/k4CzC4O3PGf1GdWhHmkENOgd23GqI/yOTKQq5Z5ZgAp
 rZzcXiuCSq1QkLME7ElxoOLQhs+fYiVGoAM/maxLa+2g4M1Y/YlHBDGhG4RB4lLA
 3zugbyJ15tNfgNuRvCB4x304WkCp5VDlcsCiKq18LFcrkz1SYGj5LwG/bswDqgkS
 0mOtZKu68NhutX8Pcy4vY3iOmMa1/Q==
 =RhHb
 -----END PGP SIGNATURE-----

Merge tag 'random_for_linus_stable' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/random

Pull /dev/random fixes from Ted Ts'o:
 "Fix some bugs in the /dev/random driver which causes getrandom(2) to
  unblock earlier than designed.

  Thanks to Jann Horn from Google's Project Zero for pointing this out
  to me"

* tag 'random_for_linus_stable' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/random:
  random: add new ioctl RNDRESEEDCRNG
  random: crng_reseed() should lock the crng instance that it is modifying
  random: set up the NUMA crng instances after the CRNG is fully initialized
  random: use a different mixing algorithm for add_device_randomness()
  random: fix crng_ready() test
This commit is contained in:
Linus Torvalds 2018-04-21 21:20:48 -07:00
commit 285848b0f4
2 changed files with 100 additions and 31 deletions

View File

@ -427,8 +427,9 @@ struct crng_state primary_crng = {
* its value (from 0->1->2). * its value (from 0->1->2).
*/ */
static int crng_init = 0; static int crng_init = 0;
#define crng_ready() (likely(crng_init > 0)) #define crng_ready() (likely(crng_init > 1))
static int crng_init_cnt = 0; static int crng_init_cnt = 0;
static unsigned long crng_global_init_time = 0;
#define CRNG_INIT_CNT_THRESH (2*CHACHA20_KEY_SIZE) #define CRNG_INIT_CNT_THRESH (2*CHACHA20_KEY_SIZE)
static void _extract_crng(struct crng_state *crng, static void _extract_crng(struct crng_state *crng,
__u32 out[CHACHA20_BLOCK_WORDS]); __u32 out[CHACHA20_BLOCK_WORDS]);
@ -787,6 +788,36 @@ static void crng_initialize(struct crng_state *crng)
crng->init_time = jiffies - CRNG_RESEED_INTERVAL - 1; crng->init_time = jiffies - CRNG_RESEED_INTERVAL - 1;
} }
#ifdef CONFIG_NUMA
static void numa_crng_init(void)
{
int i;
struct crng_state *crng;
struct crng_state **pool;
pool = kcalloc(nr_node_ids, sizeof(*pool), GFP_KERNEL|__GFP_NOFAIL);
for_each_online_node(i) {
crng = kmalloc_node(sizeof(struct crng_state),
GFP_KERNEL | __GFP_NOFAIL, i);
spin_lock_init(&crng->lock);
crng_initialize(crng);
pool[i] = crng;
}
mb();
if (cmpxchg(&crng_node_pool, NULL, pool)) {
for_each_node(i)
kfree(pool[i]);
kfree(pool);
}
}
#else
static void numa_crng_init(void) {}
#endif
/*
* crng_fast_load() can be called by code in the interrupt service
* path. So we can't afford to dilly-dally.
*/
static int crng_fast_load(const char *cp, size_t len) static int crng_fast_load(const char *cp, size_t len)
{ {
unsigned long flags; unsigned long flags;
@ -794,7 +825,7 @@ static int crng_fast_load(const char *cp, size_t len)
if (!spin_trylock_irqsave(&primary_crng.lock, flags)) if (!spin_trylock_irqsave(&primary_crng.lock, flags))
return 0; return 0;
if (crng_ready()) { if (crng_init != 0) {
spin_unlock_irqrestore(&primary_crng.lock, flags); spin_unlock_irqrestore(&primary_crng.lock, flags);
return 0; return 0;
} }
@ -813,6 +844,51 @@ static int crng_fast_load(const char *cp, size_t len)
return 1; return 1;
} }
/*
* crng_slow_load() is called by add_device_randomness, which has two
* attributes. (1) We can't trust the buffer passed to it is
* guaranteed to be unpredictable (so it might not have any entropy at
* all), and (2) it doesn't have the performance constraints of
* crng_fast_load().
*
* So we do something more comprehensive which is guaranteed to touch
* all of the primary_crng's state, and which uses a LFSR with a
* period of 255 as part of the mixing algorithm. Finally, we do
* *not* advance crng_init_cnt since buffer we may get may be something
* like a fixed DMI table (for example), which might very well be
* unique to the machine, but is otherwise unvarying.
*/
static int crng_slow_load(const char *cp, size_t len)
{
unsigned long flags;
static unsigned char lfsr = 1;
unsigned char tmp;
unsigned i, max = CHACHA20_KEY_SIZE;
const char * src_buf = cp;
char * dest_buf = (char *) &primary_crng.state[4];
if (!spin_trylock_irqsave(&primary_crng.lock, flags))
return 0;
if (crng_init != 0) {
spin_unlock_irqrestore(&primary_crng.lock, flags);
return 0;
}
if (len > max)
max = len;
for (i = 0; i < max ; i++) {
tmp = lfsr;
lfsr >>= 1;
if (tmp & 1)
lfsr ^= 0xE1;
tmp = dest_buf[i % CHACHA20_KEY_SIZE];
dest_buf[i % CHACHA20_KEY_SIZE] ^= src_buf[i % len] ^ lfsr;
lfsr += (tmp << 3) | (tmp >> 5);
}
spin_unlock_irqrestore(&primary_crng.lock, flags);
return 1;
}
static void crng_reseed(struct crng_state *crng, struct entropy_store *r) static void crng_reseed(struct crng_state *crng, struct entropy_store *r)
{ {
unsigned long flags; unsigned long flags;
@ -831,7 +907,7 @@ static void crng_reseed(struct crng_state *crng, struct entropy_store *r)
_crng_backtrack_protect(&primary_crng, buf.block, _crng_backtrack_protect(&primary_crng, buf.block,
CHACHA20_KEY_SIZE); CHACHA20_KEY_SIZE);
} }
spin_lock_irqsave(&primary_crng.lock, flags); spin_lock_irqsave(&crng->lock, flags);
for (i = 0; i < 8; i++) { for (i = 0; i < 8; i++) {
unsigned long rv; unsigned long rv;
if (!arch_get_random_seed_long(&rv) && if (!arch_get_random_seed_long(&rv) &&
@ -841,9 +917,10 @@ static void crng_reseed(struct crng_state *crng, struct entropy_store *r)
} }
memzero_explicit(&buf, sizeof(buf)); memzero_explicit(&buf, sizeof(buf));
crng->init_time = jiffies; crng->init_time = jiffies;
spin_unlock_irqrestore(&primary_crng.lock, flags); spin_unlock_irqrestore(&crng->lock, flags);
if (crng == &primary_crng && crng_init < 2) { if (crng == &primary_crng && crng_init < 2) {
invalidate_batched_entropy(); invalidate_batched_entropy();
numa_crng_init();
crng_init = 2; crng_init = 2;
process_random_ready_list(); process_random_ready_list();
wake_up_interruptible(&crng_init_wait); wake_up_interruptible(&crng_init_wait);
@ -856,8 +933,9 @@ static void _extract_crng(struct crng_state *crng,
{ {
unsigned long v, flags; unsigned long v, flags;
if (crng_init > 1 && if (crng_ready() &&
time_after(jiffies, crng->init_time + CRNG_RESEED_INTERVAL)) (time_after(crng_global_init_time, crng->init_time) ||
time_after(jiffies, crng->init_time + CRNG_RESEED_INTERVAL)))
crng_reseed(crng, crng == &primary_crng ? &input_pool : NULL); crng_reseed(crng, crng == &primary_crng ? &input_pool : NULL);
spin_lock_irqsave(&crng->lock, flags); spin_lock_irqsave(&crng->lock, flags);
if (arch_get_random_long(&v)) if (arch_get_random_long(&v))
@ -981,10 +1059,8 @@ void add_device_randomness(const void *buf, unsigned int size)
unsigned long time = random_get_entropy() ^ jiffies; unsigned long time = random_get_entropy() ^ jiffies;
unsigned long flags; unsigned long flags;
if (!crng_ready()) { if (!crng_ready() && size)
crng_fast_load(buf, size); crng_slow_load(buf, size);
return;
}
trace_add_device_randomness(size, _RET_IP_); trace_add_device_randomness(size, _RET_IP_);
spin_lock_irqsave(&input_pool.lock, flags); spin_lock_irqsave(&input_pool.lock, flags);
@ -1139,7 +1215,7 @@ void add_interrupt_randomness(int irq, int irq_flags)
fast_mix(fast_pool); fast_mix(fast_pool);
add_interrupt_bench(cycles); add_interrupt_bench(cycles);
if (!crng_ready()) { if (unlikely(crng_init == 0)) {
if ((fast_pool->count >= 64) && if ((fast_pool->count >= 64) &&
crng_fast_load((char *) fast_pool->pool, crng_fast_load((char *) fast_pool->pool,
sizeof(fast_pool->pool))) { sizeof(fast_pool->pool))) {
@ -1680,28 +1756,10 @@ static void init_std_data(struct entropy_store *r)
*/ */
static int rand_initialize(void) static int rand_initialize(void)
{ {
#ifdef CONFIG_NUMA
int i;
struct crng_state *crng;
struct crng_state **pool;
#endif
init_std_data(&input_pool); init_std_data(&input_pool);
init_std_data(&blocking_pool); init_std_data(&blocking_pool);
crng_initialize(&primary_crng); crng_initialize(&primary_crng);
crng_global_init_time = jiffies;
#ifdef CONFIG_NUMA
pool = kcalloc(nr_node_ids, sizeof(*pool), GFP_KERNEL|__GFP_NOFAIL);
for_each_online_node(i) {
crng = kmalloc_node(sizeof(struct crng_state),
GFP_KERNEL | __GFP_NOFAIL, i);
spin_lock_init(&crng->lock);
crng_initialize(crng);
pool[i] = crng;
}
mb();
crng_node_pool = pool;
#endif
return 0; return 0;
} }
early_initcall(rand_initialize); early_initcall(rand_initialize);
@ -1875,6 +1933,14 @@ static long random_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
input_pool.entropy_count = 0; input_pool.entropy_count = 0;
blocking_pool.entropy_count = 0; blocking_pool.entropy_count = 0;
return 0; return 0;
case RNDRESEEDCRNG:
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
if (crng_init < 2)
return -ENODATA;
crng_reseed(&primary_crng, NULL);
crng_global_init_time = jiffies - 1;
return 0;
default: default:
return -EINVAL; return -EINVAL;
} }
@ -2212,7 +2278,7 @@ void add_hwgenerator_randomness(const char *buffer, size_t count,
{ {
struct entropy_store *poolp = &input_pool; struct entropy_store *poolp = &input_pool;
if (!crng_ready()) { if (unlikely(crng_init == 0)) {
crng_fast_load(buffer, count); crng_fast_load(buffer, count);
return; return;
} }

View File

@ -35,6 +35,9 @@
/* Clear the entropy pool and associated counters. (Superuser only.) */ /* Clear the entropy pool and associated counters. (Superuser only.) */
#define RNDCLEARPOOL _IO( 'R', 0x06 ) #define RNDCLEARPOOL _IO( 'R', 0x06 )
/* Reseed CRNG. (Superuser only.) */
#define RNDRESEEDCRNG _IO( 'R', 0x07 )
struct rand_pool_info { struct rand_pool_info {
int entropy_count; int entropy_count;
int buf_size; int buf_size;