|
Message-ID: <d2ed1821-96a2-d7eb-4c1e-4787c7dd467a@c-s.fr> Date: Mon, 29 Jul 2019 13:33:29 +0200 From: Christophe Leroy <christophe.leroy@....fr> To: Jason Yan <yanaijie@...wei.com>, mpe@...erman.id.au, linuxppc-dev@...ts.ozlabs.org, diana.craciun@....com, benh@...nel.crashing.org, paulus@...ba.org, npiggin@...il.com, keescook@...omium.org, kernel-hardening@...ts.openwall.com Cc: linux-kernel@...r.kernel.org, wangkefeng.wang@...wei.com, yebin10@...wei.com, thunder.leizhen@...wei.com, jingxiangfeng@...wei.com, fanchengyang@...wei.com Subject: Re: [RFC PATCH 07/10] powerpc/fsl_booke/32: randomize the kernel image offset Le 17/07/2019 à 10:06, Jason Yan a écrit : > After we have the basic support of relocate the kernel in some > appropriate place, we can start to randomize the offset now. > > Entropy is derived from the banner and timer, which will change every > build and boot. This not so much safe so additionally the bootloader may > pass entropy via the /chosen/kaslr-seed node in device tree. > > We will use the first 512M of the low memory to randomize the kernel > image. The memory will be split in 64M zones. We will use the lower 8 > bit of the entropy to decide the index of the 64M zone. Then we chose a > 16K aligned offset inside the 64M zone to put the kernel in. > > KERNELBASE > > |--> 64M <--| > | | > +---------------+ +----------------+---------------+ > | |....| |kernel| | | > +---------------+ +----------------+---------------+ > | | > |-----> offset <-----| > > kimage_vaddr > > We also check if we will overlap with some areas like the dtb area, the > initrd area or the crashkernel area. If we cannot find a proper area, > kaslr will be disabled and boot from the original kernel. > > Signed-off-by: Jason Yan <yanaijie@...wei.com> > Cc: Diana Craciun <diana.craciun@....com> > Cc: Michael Ellerman <mpe@...erman.id.au> > Cc: Christophe Leroy <christophe.leroy@....fr> > Cc: Benjamin Herrenschmidt <benh@...nel.crashing.org> > Cc: Paul Mackerras <paulus@...ba.org> > Cc: Nicholas Piggin <npiggin@...il.com> > Cc: Kees Cook <keescook@...omium.org> > --- > arch/powerpc/kernel/kaslr_booke.c | 335 +++++++++++++++++++++++++++++- > 1 file changed, 333 insertions(+), 2 deletions(-) > > diff --git a/arch/powerpc/kernel/kaslr_booke.c b/arch/powerpc/kernel/kaslr_booke.c > index 72d8e9432048..90357f4bd313 100644 > --- a/arch/powerpc/kernel/kaslr_booke.c > +++ b/arch/powerpc/kernel/kaslr_booke.c > @@ -22,6 +22,8 @@ > #include <linux/delay.h> > #include <linux/highmem.h> > #include <linux/memblock.h> > +#include <linux/libfdt.h> > +#include <linux/crash_core.h> > #include <asm/pgalloc.h> > #include <asm/prom.h> > #include <asm/io.h> > @@ -33,15 +35,342 @@ > #include <asm/machdep.h> > #include <asm/setup.h> > #include <asm/paca.h> > +#include <asm/kdump.h> > #include <mm/mmu_decl.h> > +#include <generated/compile.h> > +#include <generated/utsrelease.h> > + > +#ifdef DEBUG > +#define DBG(fmt...) printk(KERN_ERR fmt) > +#else > +#define DBG(fmt...) > +#endif > + > +struct regions { > + unsigned long pa_start; > + unsigned long pa_end; > + unsigned long kernel_size; > + unsigned long dtb_start; > + unsigned long dtb_end; > + unsigned long initrd_start; > + unsigned long initrd_end; > + unsigned long crash_start; > + unsigned long crash_end; > + int reserved_mem; > + int reserved_mem_addr_cells; > + int reserved_mem_size_cells; > +}; > > extern int is_second_reloc; > > +/* Simplified build-specific string for starting entropy. */ > +static const char build_str[] = UTS_RELEASE " (" LINUX_COMPILE_BY "@" > + LINUX_COMPILE_HOST ") (" LINUX_COMPILER ") " UTS_VERSION; > +static char __initdata early_command_line[COMMAND_LINE_SIZE]; > + > +static __init void kaslr_get_cmdline(void *fdt) > +{ > + const char *cmdline = CONFIG_CMDLINE; > + if (!IS_ENABLED(CONFIG_CMDLINE_FORCE)) { > + int node; > + const u8 *prop; > + node = fdt_path_offset(fdt, "/chosen"); > + if (node < 0) > + goto out; > + > + prop = fdt_getprop(fdt, node, "bootargs", NULL); > + if (!prop) > + goto out; > + cmdline = prop; > + } > +out: > + strscpy(early_command_line, cmdline, COMMAND_LINE_SIZE); > +} > + Can you explain why we need that and can't use the already existing cmdline stuff ? Christophe > +static unsigned long __init rotate_xor(unsigned long hash, const void *area, > + size_t size) > +{ > + size_t i; > + unsigned long *ptr = (unsigned long *)area; > + > + for (i = 0; i < size / sizeof(hash); i++) { > + /* Rotate by odd number of bits and XOR. */ > + hash = (hash << ((sizeof(hash) * 8) - 7)) | (hash >> 7); > + hash ^= ptr[i]; > + } > + > + return hash; > +} > + > +/* Attempt to create a simple but unpredictable starting entropy. */ > +static unsigned long __init get_boot_seed(void *fdt) > +{ > + unsigned long hash = 0; > + > + hash = rotate_xor(hash, build_str, sizeof(build_str)); > + hash = rotate_xor(hash, fdt, fdt_totalsize(fdt)); > + > + return hash; > +} > + > +static __init u64 get_kaslr_seed(void *fdt) > +{ > + int node, len; > + fdt64_t *prop; > + u64 ret; > + > + node = fdt_path_offset(fdt, "/chosen"); > + if (node < 0) > + return 0; > + > + prop = fdt_getprop_w(fdt, node, "kaslr-seed", &len); > + if (!prop || len != sizeof(u64)) > + return 0; > + > + ret = fdt64_to_cpu(*prop); > + *prop = 0; > + return ret; > +} > + > +static __init bool regions_overlap(u32 s1, u32 e1, u32 s2, u32 e2) > +{ > + return e1 >= s2 && e2 >= s1; > +} > + > +static __init bool overlaps_reserved_region(const void *fdt, u32 start, > + u32 end, struct regions *regions) > +{ > + int subnode, len, i; > + u64 base, size; > + > + /* check for overlap with /memreserve/ entries */ > + for (i = 0; i < fdt_num_mem_rsv(fdt); i++) { > + if (fdt_get_mem_rsv(fdt, i, &base, &size) < 0) > + continue; > + if (regions_overlap(start, end, base, base + size)) > + return true; > + } > + > + if (regions->reserved_mem < 0) > + return false; > + > + /* check for overlap with static reservations in /reserved-memory */ > + for (subnode = fdt_first_subnode(fdt, regions->reserved_mem); > + subnode >= 0; > + subnode = fdt_next_subnode(fdt, subnode)) { > + const fdt32_t *reg; > + u64 rsv_end; > + > + len = 0; > + reg = fdt_getprop(fdt, subnode, "reg", &len); > + while (len >= (regions->reserved_mem_addr_cells + > + regions->reserved_mem_size_cells)) { > + base = fdt32_to_cpu(reg[0]); > + if (regions->reserved_mem_addr_cells == 2) > + base = (base << 32) | fdt32_to_cpu(reg[1]); > + > + reg += regions->reserved_mem_addr_cells; > + len -= 4 * regions->reserved_mem_addr_cells; > + > + size = fdt32_to_cpu(reg[0]); > + if (regions->reserved_mem_size_cells == 2) > + size = (size << 32) | fdt32_to_cpu(reg[1]); > + > + reg += regions->reserved_mem_size_cells; > + len -= 4 * regions->reserved_mem_size_cells; > + > + if (base >= regions->pa_end) > + continue; > + > + rsv_end = min(base + size, (u64)U32_MAX); > + > + if (regions_overlap(start, end, base, rsv_end)) > + return true; > + } > + } > + return false; > +} > + > +static __init bool overlaps_region(const void *fdt, u32 start, > + u32 end, struct regions *regions) > +{ > + if (regions_overlap(start, end, regions->dtb_start, > + regions->dtb_end)) > + return true; > + > + if (regions_overlap(start, end, regions->initrd_start, > + regions->initrd_end)) > + return true; > + > + if (regions_overlap(start, end, regions->crash_start, > + regions->crash_end)) > + return true; > + > + return overlaps_reserved_region(fdt, start, end, regions); > +} > + > +static void __init get_crash_kernel(void *fdt, unsigned long size, > + struct regions *regions) > +{ > +#ifdef CONFIG_KEXEC_CORE > + unsigned long long crash_size, crash_base; > + int ret; > + > + ret = parse_crashkernel(early_command_line, size, &crash_size, > + &crash_base); > + if (ret != 0 || crash_size == 0) > + return; > + if (crash_base == 0) > + crash_base = KDUMP_KERNELBASE; > + > + regions->crash_start = (unsigned long)crash_base; > + regions->crash_end = (unsigned long)(crash_base + crash_size); > + > + DBG("crash_base=0x%llx crash_size=0x%llx\n", crash_base, crash_size); > +#endif > +} > + > +static void __init get_initrd_range(void *fdt, struct regions *regions) > +{ > + u64 start, end; > + int node, len; > + const __be32 *prop; > + > + node = fdt_path_offset(fdt, "/chosen"); > + if (node < 0) > + return; > + > + prop = fdt_getprop(fdt, node, "linux,initrd-start", &len); > + if (!prop) > + return; > + start = of_read_number(prop, len / 4); > + > + prop = fdt_getprop(fdt, node, "linux,initrd-end", &len); > + if (!prop) > + return; > + end = of_read_number(prop, len / 4); > + > + regions->initrd_start = (unsigned long)start; > + regions->initrd_end = (unsigned long)end; > + > + DBG("initrd_start=0x%llx initrd_end=0x%llx\n", start, end); > +} > + > +static __init unsigned long get_usable_offset(const void *fdt, struct regions *regions, > + unsigned long start) > +{ > + unsigned long pa; > + unsigned long pa_end; > + > + for (pa = start; pa > regions->pa_start; pa -= SZ_16K) { > + pa_end = pa + regions->kernel_size; > + if (overlaps_region(fdt, pa, pa_end, regions)) > + continue; > + > + return pa; > + } > + return 0; > +} > + > +static __init void get_cell_sizes(const void *fdt, int node, int *addr_cells, > + int *size_cells) > +{ > + const int *prop; > + int len; > + > + /* > + * Retrieve the #address-cells and #size-cells properties > + * from the 'node', or use the default if not provided. > + */ > + *addr_cells = *size_cells = 1; > + > + prop = fdt_getprop(fdt, node, "#address-cells", &len); > + if (len == 4) > + *addr_cells = fdt32_to_cpu(*prop); > + prop = fdt_getprop(fdt, node, "#size-cells", &len); > + if (len == 4) > + *size_cells = fdt32_to_cpu(*prop); > +} > + > static unsigned long __init kaslr_choose_location(void *dt_ptr, phys_addr_t size, > unsigned long kernel_sz) > { > - /* return a fixed offset of 64M for now */ > - return 0x4000000; > + unsigned long offset, random; > + unsigned long ram, linear_sz; > + unsigned long kaslr_offset; > + u64 seed; > + struct regions regions; > + unsigned long index; > + > + random = get_boot_seed(dt_ptr); > + > + seed = get_tb() << 32; > + seed ^= get_tb(); > + random = rotate_xor(random, &seed, sizeof(seed)); > + > + /* > + * Retrieve (and wipe) the seed from the FDT > + */ > + seed = get_kaslr_seed(dt_ptr); > + if (seed) > + random = rotate_xor(random, &seed, sizeof(seed)); > + > + ram = min((phys_addr_t)__max_low_memory, size); > + ram = map_mem_in_cams(ram, CONFIG_LOWMEM_CAM_NUM, true); > + linear_sz = min(ram, (unsigned long)SZ_512M); > + > + /* If the linear size is smaller than 64M, do not randmize */ > + if (linear_sz < SZ_64M) > + return 0; > + > + memset(®ions, 0, sizeof(regions)); > + > + /* check for a reserved-memory node and record its cell sizes */ > + regions.reserved_mem = fdt_path_offset(dt_ptr, "/reserved-memory"); > + if (regions.reserved_mem >= 0) > + get_cell_sizes(dt_ptr, regions.reserved_mem, > + ®ions.reserved_mem_addr_cells, > + ®ions.reserved_mem_size_cells); > + > + regions.pa_start = 0; > + regions.pa_end = linear_sz; > + regions.dtb_start = __pa(dt_ptr); > + regions.dtb_end = __pa(dt_ptr) + fdt_totalsize(dt_ptr); > + regions.kernel_size = kernel_sz; > + > + get_initrd_range(dt_ptr, ®ions); > + get_crash_kernel(dt_ptr, ram, ®ions); > + > + /* > + * Decide which 64M we want to start > + * Only use the low 8 bits of the random seed > + */ > + index = random & 0xFF; > + index %= linear_sz / SZ_64M; > + > + /* Decide offset inside 64M */ > + if (index == 0) { > + offset = random % (SZ_64M - round_up(kernel_sz, SZ_16K) * 2); > + offset += round_up(kernel_sz, SZ_16K); > + offset = round_up(offset, SZ_16K); > + } else { > + offset = random % (SZ_64M - kernel_sz); > + offset = round_down(offset, SZ_16K); > + } > + > + while (index >= 0) { > + offset = offset + index * SZ_64M; > + kaslr_offset = get_usable_offset(dt_ptr, ®ions, offset); > + if (kaslr_offset) > + break; > + index--; > + } > + > + /* Did not find any usable region? Give up randomize */ > + if (index < 0) > + kaslr_offset = 0; > + > + return kaslr_offset; > } > > /* > @@ -58,6 +387,8 @@ notrace void __init kaslr_early_init(void *dt_ptr, phys_addr_t size) > > kernel_sz = (unsigned long)_end - KERNELBASE; > > + kaslr_get_cmdline(dt_ptr); > + > offset = kaslr_choose_location(dt_ptr, size, kernel_sz); > > if (offset == 0) >
Powered by blists - more mailing lists
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.