random: fix some data races
Subsystems (3)
v2 cover random: fix some data races
This series fixes some data races in random.c.
Changed v1 => v2:
- Remove unneeded 'inline' keywords
- Use READ_ONCE() instead of smp_load_acquire()
- Updated commit message
- Added patch to fix data race on crng init time
Eric Biggers (2):
random: fix data race on crng_node_pool
random: fix data race on crng init time
drivers/char/random.c | 61 +++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 28 deletions(-)
Signature · 1 line
2.34.1
PATCH v2 1/2 random: fix data race on crng_node_pool
From: Eric Biggers <[email protected]>
extract_crng() and crng_backtrack_protect() load crng_node_pool with a plain load, which causes undefined behavior if do_numa_crng_init() modifies it concurrently.
Fix this by using READ_ONCE(). Note: as per the previous discussion
https://lore.kernel.org/lkml/[email protected]/T/#u,READ_ONCE() is believed to be sufficient here, and it was requested that it be used here instead of smp_load_acquire().
Also change do_numa_crng_init() to set crng_node_pool using cmpxchg_release() instead of mb() + cmpxchg(), as the former is sufficient here but is more lightweight.
--- drivers/char/random.c | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-)
drivers/char/random.c +22 -20
@@ -843,8 +843,8 @@ static void do_numa_crng_init(struct work_struct *work)crng_initialize_secondary (crng); pool[i] = crng; } -mb (); -if (cmpxchg (&crng_node_pool,NULL , pool)) { +/* pairs with READ_ONCE() in select_crng() */ +if (cmpxchg_release (&crng_node_pool,NULL , pool) !=NULL ) {for_each_node (i)kfree (pool[i]);kfree (pool);
@@ -857,8 +857,26 @@ static void numa_crng_init(void) {schedule_work (&numa_crng_init_work); } + +static struct crng_state *select_crng (void ) +{ +struct crng_state **pool; +int nid =numa_node_id (); + +/* pairs with cmpxchg_release() in do_numa_crng_init() */ + pool =READ_ONCE (crng_node_pool); +if (pool && pool[nid]) +return pool[nid]; + +return &primary_crng; +}#else static void numa_crng_init (void ) {} + +static struct crng_state *select_crng (void ) +{ +return &primary_crng; +}#endif /*
@@ -1005,15 +1023,7 @@ static void _extract_crng(struct crng_state *crng,static void extract_crng (__u8 out[CHACHA_BLOCK_SIZE ]) { -struct crng_state *crng =NULL ; - -#ifdef CONFIG_NUMA -if (crng_node_pool) - crng = crng_node_pool[numa_node_id ()]; - if (crng == NULL) -#endif - crng = &primary_crng; -_extract_crng (crng, out); +_extract_crng (select_crng (), out); }/*
@@ -1042,15 +1052,7 @@ static void _crng_backtrack_protect(struct crng_state *crng,static void crng_backtrack_protect (__u8 tmp[CHACHA_BLOCK_SIZE ],int used) { -struct crng_state *crng =NULL ; - -#ifdef CONFIG_NUMA -if (crng_node_pool) - crng = crng_node_pool[numa_node_id ()]; - if (crng == NULL) -#endif - crng = &primary_crng; -_crng_backtrack_protect (crng, tmp, used); +_crng_backtrack_protect (select_crng (), tmp, used); }static ssize_t extract_crng_user (void __user *buf,size_t nbytes)
Signature · 1 line
2.34.1
PATCH v2 2/2 random: fix data race on crng init time
In reply to Eric BiggersFrom: Eric Biggers <[email protected]>
_extract_crng() does plain loads of crng->init_time and crng_global_init_time, which causes undefined behavior if crng_reseed() and RNDRESEEDCRNG modify these corrently.
Use READ_ONCE() and WRITE_ONCE() to make the behavior defined.
Don't fix the race on crng->init_time by protecting it with crng->lock, since it's not a problem for duplicate reseedings to occur. I.e., the lockless access with READ_ONCE() is fine.
--- drivers/char/random.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-)
drivers/char/random.c +10 -7
@@ -980,7 +980,7 @@ static void crng_reseed(struct crng_state *crng, struct entropy_store *r) crng->state[i+4 ] ^= buf.key[i] ^ rv; }memzero_explicit (&buf,sizeof (buf)); - crng->init_time = jiffies; +WRITE_ONCE (crng->init_time, jiffies);spin_unlock_irqrestore (&crng->lock, flags);if (crng == &primary_crng && crng_init <2 ) {invalidate_batched_entropy ();
@@ -1006,12 +1006,15 @@ static void crng_reseed(struct crng_state *crng, struct entropy_store *r)static void _extract_crng (struct crng_state *crng,__u8 out[CHACHA_BLOCK_SIZE ]) { -unsigned long v, flags; +unsigned long v, flags, init_time; -if (crng_ready () && - (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 ); +if (crng_ready ()) { + init_time =READ_ONCE (crng->init_time); +if (time_after (READ_ONCE (crng_global_init_time), init_time) || +time_after (jiffies, init_time +CRNG_RESEED_INTERVAL )) +crng_reseed (crng, crng == &primary_crng ? + &input_pool :NULL ); + }spin_lock_irqsave (&crng->lock, flags);if (arch_get_random_long (&v)) crng->state[14 ] ^= v;
@@ -1951,7 +1954,7 @@ static long random_ioctl(struct file *f, unsigned int cmd, unsigned long arg)if (crng_init <2 )return -ENODATA ;crng_reseed (&primary_crng, &input_pool); - crng_global_init_time = jiffies -1 ; +WRITE_ONCE (crng_global_init_time, jiffies -1 );return 0 ;default :return -EINVAL ;
Signature · 1 line
2.34.1
Quoted 11 lines · Eric Biggers
This series fixes some data races in
random.c.Changed v1 => v2:
- Remove unneeded 'inline' keywords
- UseREAD_ONCE()instead ofsmp_load_acquire()
- Updated commit message
- Added patch to fix data race on crng init timeEric Biggers (2):
random: fix data race oncrng_node_pool
random: fix data race on crng init time
From a memory-ordering viewpoint:
Quoted 6 lines
drivers/char/random.c | 61 +++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 28 deletions(-)Signature · 1 line
2.34.1
Jason A. Donenfeld
maintainerAll applied to crng/random.git. Thanks for the series.
Jason