Skip to content

Commit f623849

Browse files
committed
random: split initialization into early step and later step
The full RNG initialization relies on some timestamps, made possible with initialization functions like time_init() and timekeeping_init(). However, these are only available rather late in initialization. Meanwhile, other things, such as memory allocator functions, make use of the RNG much earlier. So split RNG initialization into two phases. We can provide arch randomness very early on, and then later, after timekeeping and such are available, initialize the rest. This ensures that, for example, slabs are properly randomized if RDRAND is available. Without this, CONFIG_SLAB_FREELIST_RANDOM=y loses a degree of its security, because its random seed is potentially deterministic, since it hasn't yet incorporated RDRAND. It also makes it possible to use a better seed in kfence, which currently relies on only the cycle counter. Another positive consequence is that on systems with RDRAND, running with CONFIG_WARN_ALL_UNSEEDED_RANDOM=y results in no warnings at all. One subtle side effect of this change is that on systems with no RDRAND, RDTSC is now only queried by random_init() once, committing the moment of the function call, instead of multiple times as before. This is intentional, as the multiple RDTSCs in a loop before weren't accomplishing very much, with jitter being better provided by try_to_generate_entropy(). Plus, filling blocks with RDTSC is still being done in extract_entropy(), which is necessarily called before random bytes are served anyway. Cc: Andrew Morton <[email protected]> Reviewed-by: Kees Cook <[email protected]> Reviewed-by: Dominik Brodowski <[email protected]> Signed-off-by: Jason A. Donenfeld <[email protected]>
1 parent 748bc4d commit f623849

File tree

3 files changed

+40
-30
lines changed

3 files changed

+40
-30
lines changed

drivers/char/random.c

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -772,18 +772,13 @@ static int random_pm_notification(struct notifier_block *nb, unsigned long actio
772772
static struct notifier_block pm_notifier = { .notifier_call = random_pm_notification };
773773

774774
/*
775-
* The first collection of entropy occurs at system boot while interrupts
776-
* are still turned off. Here we push in latent entropy, RDSEED, a timestamp,
777-
* utsname(), and the command line. Depending on the above configuration knob,
778-
* RDSEED may be considered sufficient for initialization. Note that much
779-
* earlier setup may already have pushed entropy into the input pool by the
780-
* time we get here.
775+
* This is called extremely early, before time keeping functionality is
776+
* available, but arch randomness is. Interrupts are not yet enabled.
781777
*/
782-
int __init random_init(const char *command_line)
778+
void __init random_init_early(const char *command_line)
783779
{
784-
ktime_t now = ktime_get_real();
785-
size_t i, longs, arch_bits;
786780
unsigned long entropy[BLAKE2S_BLOCK_SIZE / sizeof(long)];
781+
size_t i, longs, arch_bits;
787782

788783
#if defined(LATENT_ENTROPY_PLUGIN)
789784
static const u8 compiletime_seed[BLAKE2S_BLOCK_SIZE] __initconst __latent_entropy;
@@ -803,34 +798,49 @@ int __init random_init(const char *command_line)
803798
i += longs;
804799
continue;
805800
}
806-
entropy[0] = random_get_entropy();
807-
_mix_pool_bytes(entropy, sizeof(*entropy));
808801
arch_bits -= sizeof(*entropy) * 8;
809802
++i;
810803
}
811-
_mix_pool_bytes(&now, sizeof(now));
812-
_mix_pool_bytes(utsname(), sizeof(*(utsname())));
804+
813805
_mix_pool_bytes(command_line, strlen(command_line));
806+
807+
/* Reseed if already seeded by earlier phases. */
808+
if (crng_ready())
809+
crng_reseed();
810+
else if (trust_cpu)
811+
_credit_init_bits(arch_bits);
812+
}
813+
814+
/*
815+
* This is called a little bit after the prior function, and now there is
816+
* access to timestamps counters. Interrupts are not yet enabled.
817+
*/
818+
void __init random_init(void)
819+
{
820+
unsigned long entropy = random_get_entropy();
821+
ktime_t now = ktime_get_real();
822+
823+
_mix_pool_bytes(utsname(), sizeof(*(utsname())));
824+
_mix_pool_bytes(&now, sizeof(now));
825+
_mix_pool_bytes(&entropy, sizeof(entropy));
814826
add_latent_entropy();
815827

816828
/*
817-
* If we were initialized by the bootloader before jump labels are
818-
* initialized, then we should enable the static branch here, where
829+
* If we were initialized by the cpu or bootloader before jump labels
830+
* are initialized, then we should enable the static branch here, where
819831
* it's guaranteed that jump labels have been initialized.
820832
*/
821833
if (!static_branch_likely(&crng_is_ready) && crng_init >= CRNG_READY)
822834
crng_set_ready(NULL);
823835

836+
/* Reseed if already seeded by earlier phases. */
824837
if (crng_ready())
825838
crng_reseed();
826-
else if (trust_cpu)
827-
_credit_init_bits(arch_bits);
828839

829840
WARN_ON(register_pm_notifier(&pm_notifier));
830841

831-
WARN(!random_get_entropy(), "Missing cycle counter and fallback timer; RNG "
832-
"entropy collection will consequently suffer.");
833-
return 0;
842+
WARN(!entropy, "Missing cycle counter and fallback timer; RNG "
843+
"entropy collection will consequently suffer.");
834844
}
835845

836846
/*

include/linux/random.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ static inline unsigned long get_random_canary(void)
7272
return get_random_long() & CANARY_MASK;
7373
}
7474

75-
int __init random_init(const char *command_line);
75+
void __init random_init_early(const char *command_line);
76+
void __init random_init(void);
7677
bool rng_is_initialized(void);
7778
int wait_for_random_bytes(void);
7879

init/main.c

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -976,6 +976,9 @@ asmlinkage __visible void __init __no_sanitize_address start_kernel(void)
976976
parse_args("Setting extra init args", extra_init_args,
977977
NULL, 0, -1, -1, NULL, set_init_arg);
978978

979+
/* Architectural and non-timekeeping rng init, before allocator init */
980+
random_init_early(command_line);
981+
979982
/*
980983
* These use large bootmem allocations and must precede
981984
* kmem_cache_init()
@@ -1035,17 +1038,13 @@ asmlinkage __visible void __init __no_sanitize_address start_kernel(void)
10351038
hrtimers_init();
10361039
softirq_init();
10371040
timekeeping_init();
1038-
kfence_init();
10391041
time_init();
10401042

1041-
/*
1042-
* For best initial stack canary entropy, prepare it after:
1043-
* - setup_arch() for any UEFI RNG entropy and boot cmdline access
1044-
* - timekeeping_init() for ktime entropy used in random_init()
1045-
* - time_init() for making random_get_entropy() work on some platforms
1046-
* - random_init() to initialize the RNG from from early entropy sources
1047-
*/
1048-
random_init(command_line);
1043+
/* This must be after timekeeping is initialized */
1044+
random_init();
1045+
1046+
/* These make use of the fully initialized rng */
1047+
kfence_init();
10491048
boot_init_stack_canary();
10501049

10511050
perf_event_init();

0 commit comments

Comments
 (0)