Skip to content

Commit 68b8e97

Browse files
zx2c4suryasaimadhu
authored andcommitted
x86/setup: Use rng seeds from setup_data
Currently, the only way x86 can get an early boot RNG seed is via EFI, which is generally always used now for physical machines, but is very rarely used in VMs, especially VMs that are optimized for starting "instantaneously", such as Firecracker's MicroVM. For tiny fast booting VMs, EFI is not something you generally need or want. Rather, the image loader or firmware should be able to pass a single random seed, exactly as device tree platforms do with the "rng-seed" property. Additionally, this is something that bootloaders can append, with their own seed file management, which is something every other major OS ecosystem has that Linux does not (yet). Add SETUP_RNG_SEED, similar to the other eight setup_data entries that are parsed at boot. It also takes care to zero out the seed immediately after using, in order to retain forward secrecy. This all takes about 7 trivial lines of code. Then, on kexec_file_load(), a new fresh seed is generated and passed to the next kernel, just as is done on device tree architectures when using kexec. And, importantly, I've tested that QEMU is able to properly pass SETUP_RNG_SEED as well, making this work for every step of the way. This code too is pretty straight forward. Together these measures ensure that VMs and nested kexec()'d kernels always receive a proper boot time RNG seed at the earliest possible stage from their parents: - Host [already has strongly initialized RNG] - QEMU [passes fresh seed in SETUP_RNG_SEED field] - Linux [uses parent's seed and gathers entropy of its own] - kexec [passes this in SETUP_RNG_SEED field] - Linux [uses parent's seed and gathers entropy of its own] - kexec [passes this in SETUP_RNG_SEED field] - Linux [uses parent's seed and gathers entropy of its own] - kexec [passes this in SETUP_RNG_SEED field] - ... I've verified in several scenarios that this works quite well from a host kernel to QEMU and down inwards, mixing and matching loaders, with every layer providing a seed to the next. [ bp: Massage commit message. ] Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com> Signed-off-by: Borislav Petkov <bp@suse.de> Acked-by: H. Peter Anvin (Intel) <hpa@zytor.com> Link: https://lore.kernel.org/r/20220630113300.1892799-1-Jason@zx2c4.com
1 parent 5a88c48 commit 68b8e97

File tree

3 files changed

+47
-7
lines changed

3 files changed

+47
-7
lines changed

arch/x86/include/uapi/asm/bootparam.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@
1212
#define SETUP_JAILHOUSE 6
1313
#define SETUP_CC_BLOB 7
1414
#define SETUP_IMA 8
15+
#define SETUP_RNG_SEED 9
16+
#define SETUP_ENUM_MAX SETUP_RNG_SEED
1517

1618
#define SETUP_INDIRECT (1<<31)
17-
18-
/* SETUP_INDIRECT | max(SETUP_*) */
19-
#define SETUP_TYPE_MAX (SETUP_INDIRECT | SETUP_CC_BLOB)
19+
#define SETUP_TYPE_MAX (SETUP_ENUM_MAX | SETUP_INDIRECT)
2020

2121
/* ram_size flags */
2222
#define RAMDISK_IMAGE_START_MASK 0x07FF

arch/x86/kernel/kexec-bzimage64.c

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <linux/mm.h>
1919
#include <linux/efi.h>
2020
#include <linux/verification.h>
21+
#include <linux/random.h>
2122

2223
#include <asm/bootparam.h>
2324
#include <asm/setup.h>
@@ -110,6 +111,26 @@ static int setup_e820_entries(struct boot_params *params)
110111
return 0;
111112
}
112113

114+
enum { RNG_SEED_LENGTH = 32 };
115+
116+
static void
117+
setup_rng_seed(struct boot_params *params, unsigned long params_load_addr,
118+
unsigned int rng_seed_setup_data_offset)
119+
{
120+
struct setup_data *sd = (void *)params + rng_seed_setup_data_offset;
121+
unsigned long setup_data_phys;
122+
123+
if (!rng_is_initialized())
124+
return;
125+
126+
sd->type = SETUP_RNG_SEED;
127+
sd->len = RNG_SEED_LENGTH;
128+
get_random_bytes(sd->data, RNG_SEED_LENGTH);
129+
setup_data_phys = params_load_addr + rng_seed_setup_data_offset;
130+
sd->next = params->hdr.setup_data;
131+
params->hdr.setup_data = setup_data_phys;
132+
}
133+
113134
#ifdef CONFIG_EFI
114135
static int setup_efi_info_memmap(struct boot_params *params,
115136
unsigned long params_load_addr,
@@ -277,9 +298,16 @@ setup_boot_parameters(struct kimage *image, struct boot_params *params,
277298
sizeof(struct efi_setup_data);
278299
#endif
279300

280-
/* Setup IMA log buffer state */
281-
setup_ima_state(image, params, params_load_addr,
282-
setup_data_offset);
301+
if (IS_ENABLED(CONFIG_IMA_KEXEC)) {
302+
/* Setup IMA log buffer state */
303+
setup_ima_state(image, params, params_load_addr,
304+
setup_data_offset);
305+
setup_data_offset += sizeof(struct setup_data) +
306+
sizeof(struct ima_setup_data);
307+
}
308+
309+
/* Setup RNG seed */
310+
setup_rng_seed(params, params_load_addr, setup_data_offset);
283311

284312
/* Setup EDD info */
285313
memcpy(params->eddbuf, boot_params.eddbuf,
@@ -435,7 +463,9 @@ static void *bzImage64_load(struct kimage *image, char *kernel,
435463
params_cmdline_sz = ALIGN(params_cmdline_sz, 16);
436464
kbuf.bufsz = params_cmdline_sz + ALIGN(efi_map_sz, 16) +
437465
sizeof(struct setup_data) +
438-
sizeof(struct efi_setup_data);
466+
sizeof(struct efi_setup_data) +
467+
sizeof(struct setup_data) +
468+
RNG_SEED_LENGTH;
439469

440470
if (IS_ENABLED(CONFIG_IMA_KEXEC))
441471
kbuf.bufsz += sizeof(struct setup_data) +

arch/x86/kernel/setup.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <linux/usb/xhci-dbgp.h>
2525
#include <linux/static_call.h>
2626
#include <linux/swiotlb.h>
27+
#include <linux/random.h>
2728

2829
#include <uapi/linux/mount.h>
2930

@@ -418,6 +419,15 @@ static void __init parse_setup_data(void)
418419
case SETUP_IMA:
419420
add_early_ima_buffer(pa_data);
420421
break;
422+
case SETUP_RNG_SEED:
423+
data = early_memremap(pa_data, data_len);
424+
add_bootloader_randomness(data->data, data->len);
425+
/* Zero seed for forward secrecy. */
426+
memzero_explicit(data->data, data->len);
427+
/* Zero length in case we find ourselves back here by accident. */
428+
memzero_explicit(&data->len, sizeof(data->len));
429+
early_memunmap(data, data_len);
430+
break;
421431
default:
422432
break;
423433
}

0 commit comments

Comments
 (0)