Skip to content

Commit 5d73d1e

Browse files
ebiggerszx2c4
authored andcommitted
random: fix data race on crng_node_pool
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. Fixes: 1e7f583 ("random: make /dev/urandom scalable for silly userspace programs") Cc: [email protected] Signed-off-by: Eric Biggers <[email protected]> Acked-by: Paul E. McKenney <[email protected]> Signed-off-by: Jason A. Donenfeld <[email protected]>
1 parent 5320eb4 commit 5d73d1e

File tree

1 file changed

+22
-20
lines changed

1 file changed

+22
-20
lines changed

drivers/char/random.c

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -854,8 +854,8 @@ static void do_numa_crng_init(struct work_struct *work)
854854
crng_initialize_secondary(crng);
855855
pool[i] = crng;
856856
}
857-
mb();
858-
if (cmpxchg(&crng_node_pool, NULL, pool)) {
857+
/* pairs with READ_ONCE() in select_crng() */
858+
if (cmpxchg_release(&crng_node_pool, NULL, pool) != NULL) {
859859
for_each_node(i)
860860
kfree(pool[i]);
861861
kfree(pool);
@@ -868,8 +868,26 @@ static void numa_crng_init(void)
868868
{
869869
schedule_work(&numa_crng_init_work);
870870
}
871+
872+
static struct crng_state *select_crng(void)
873+
{
874+
struct crng_state **pool;
875+
int nid = numa_node_id();
876+
877+
/* pairs with cmpxchg_release() in do_numa_crng_init() */
878+
pool = READ_ONCE(crng_node_pool);
879+
if (pool && pool[nid])
880+
return pool[nid];
881+
882+
return &primary_crng;
883+
}
871884
#else
872885
static void numa_crng_init(void) {}
886+
887+
static struct crng_state *select_crng(void)
888+
{
889+
return &primary_crng;
890+
}
873891
#endif
874892

875893
/*
@@ -1016,15 +1034,7 @@ static void _extract_crng(struct crng_state *crng,
10161034

10171035
static void extract_crng(__u8 out[CHACHA_BLOCK_SIZE])
10181036
{
1019-
struct crng_state *crng = NULL;
1020-
1021-
#ifdef CONFIG_NUMA
1022-
if (crng_node_pool)
1023-
crng = crng_node_pool[numa_node_id()];
1024-
if (crng == NULL)
1025-
#endif
1026-
crng = &primary_crng;
1027-
_extract_crng(crng, out);
1037+
_extract_crng(select_crng(), out);
10281038
}
10291039

10301040
/*
@@ -1053,15 +1063,7 @@ static void _crng_backtrack_protect(struct crng_state *crng,
10531063

10541064
static void crng_backtrack_protect(__u8 tmp[CHACHA_BLOCK_SIZE], int used)
10551065
{
1056-
struct crng_state *crng = NULL;
1057-
1058-
#ifdef CONFIG_NUMA
1059-
if (crng_node_pool)
1060-
crng = crng_node_pool[numa_node_id()];
1061-
if (crng == NULL)
1062-
#endif
1063-
crng = &primary_crng;
1064-
_crng_backtrack_protect(crng, tmp, used);
1066+
_crng_backtrack_protect(select_crng(), tmp, used);
10651067
}
10661068

10671069
static ssize_t extract_crng_user(void __user *buf, size_t nbytes)

0 commit comments

Comments
 (0)