Skip to content

Commit f578055

Browse files
Merge patch series "riscv: Introduce KASLR"
Alexandre Ghiti <alexghiti@rivosinc.com> says: The following KASLR implementation allows to randomize the kernel mapping: - virtually: we expect the bootloader to provide a seed in the device-tree - physically: only implemented in the EFI stub, it relies on the firmware to provide a seed using EFI_RNG_PROTOCOL. arm64 has a similar implementation hence the patch 3 factorizes KASLR related functions for riscv to take advantage. The new virtual kernel location is limited by the early page table that only has one PUD and with the PMD alignment constraint, the kernel can only take < 512 positions. * b4-shazam-merge: riscv: libstub: Implement KASLR by using generic functions libstub: Fix compilation warning for rv32 arm64: libstub: Move KASLR handling functions to kaslr.c riscv: Dump out kernel offset information on panic riscv: Introduce virtual kernel mapping KASLR Link: https://lore.kernel.org/r/20230722123850.634544-1-alexghiti@rivosinc.com Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
2 parents f093636 + b7ac4b8 commit f578055

File tree

15 files changed

+328
-126
lines changed

15 files changed

+328
-126
lines changed

arch/arm64/include/asm/efi.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,4 +156,6 @@ static inline void efi_capsule_flush_cache_range(void *addr, int size)
156156

157157
efi_status_t efi_handle_corrupted_x18(efi_status_t s, const char *f);
158158

159+
void efi_icache_sync(unsigned long start, unsigned long end);
160+
159161
#endif /* _ASM_EFI_H */

arch/riscv/Kconfig

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,25 @@ config RELOCATABLE
720720

721721
If unsure, say N.
722722

723+
config RANDOMIZE_BASE
724+
bool "Randomize the address of the kernel image"
725+
select RELOCATABLE
726+
depends on MMU && 64BIT && !XIP_KERNEL
727+
help
728+
Randomizes the virtual address at which the kernel image is
729+
loaded, as a security feature that deters exploit attempts
730+
relying on knowledge of the location of kernel internals.
731+
732+
It is the bootloader's job to provide entropy, by passing a
733+
random u64 value in /chosen/kaslr-seed at kernel entry.
734+
735+
When booting via the UEFI stub, it will invoke the firmware's
736+
EFI_RNG_PROTOCOL implementation (if available) to supply entropy
737+
to the kernel proper. In addition, it will randomise the physical
738+
location of the kernel Image as well.
739+
740+
If unsure, say N.
741+
723742
endmenu # "Kernel features"
724743

725744
menu "Boot options"

arch/riscv/include/asm/efi.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,6 @@ void arch_efi_call_virt_teardown(void);
4545

4646
unsigned long stext_offset(void);
4747

48+
void efi_icache_sync(unsigned long start, unsigned long end);
49+
4850
#endif /* _ASM_EFI_H */

arch/riscv/include/asm/page.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ typedef struct page *pgtable_t;
106106
struct kernel_mapping {
107107
unsigned long page_offset;
108108
unsigned long virt_addr;
109+
unsigned long virt_offset;
109110
uintptr_t phys_addr;
110111
uintptr_t size;
111112
/* Offset between linear mapping virtual address and kernel load address */
@@ -185,6 +186,8 @@ extern phys_addr_t __phys_addr_symbol(unsigned long x);
185186

186187
#define sym_to_pfn(x) __phys_to_pfn(__pa_symbol(x))
187188

189+
unsigned long kaslr_offset(void);
190+
188191
#endif /* __ASSEMBLY__ */
189192

190193
#define virt_addr_valid(vaddr) ({ \

arch/riscv/kernel/image-vars.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ __efistub__start = _start;
2727
__efistub__start_kernel = _start_kernel;
2828
__efistub__end = _end;
2929
__efistub__edata = _edata;
30+
__efistub___init_text_end = __init_text_end;
3031
__efistub_screen_info = screen_info;
3132

3233
#endif

arch/riscv/kernel/pi/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,5 +35,5 @@ $(obj)/string.o: $(srctree)/lib/string.c FORCE
3535
$(obj)/ctype.o: $(srctree)/lib/ctype.c FORCE
3636
$(call if_changed_rule,cc_o_c)
3737

38-
obj-y := cmdline_early.pi.o string.pi.o ctype.pi.o lib-fdt.pi.o lib-fdt_ro.pi.o
38+
obj-y := cmdline_early.pi.o fdt_early.pi.o string.pi.o ctype.pi.o lib-fdt.pi.o lib-fdt_ro.pi.o
3939
extra-y := $(patsubst %.pi.o,%.o,$(obj-y))

arch/riscv/kernel/pi/cmdline_early.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ static char early_cmdline[COMMAND_LINE_SIZE];
1414
* LLVM complain because the function is actually unused in this file).
1515
*/
1616
u64 set_satp_mode_from_cmdline(uintptr_t dtb_pa);
17+
bool set_nokaslr_from_cmdline(uintptr_t dtb_pa);
1718

1819
static char *get_early_cmdline(uintptr_t dtb_pa)
1920
{
@@ -60,3 +61,15 @@ u64 set_satp_mode_from_cmdline(uintptr_t dtb_pa)
6061

6162
return match_noXlvl(cmdline);
6263
}
64+
65+
static bool match_nokaslr(char *cmdline)
66+
{
67+
return strstr(cmdline, "nokaslr");
68+
}
69+
70+
bool set_nokaslr_from_cmdline(uintptr_t dtb_pa)
71+
{
72+
char *cmdline = get_early_cmdline(dtb_pa);
73+
74+
return match_nokaslr(cmdline);
75+
}

arch/riscv/kernel/pi/fdt_early.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
#include <linux/types.h>
3+
#include <linux/init.h>
4+
#include <linux/libfdt.h>
5+
6+
/*
7+
* Declare the functions that are exported (but prefixed) here so that LLVM
8+
* does not complain it lacks the 'static' keyword (which, if added, makes
9+
* LLVM complain because the function is actually unused in this file).
10+
*/
11+
u64 get_kaslr_seed(uintptr_t dtb_pa);
12+
13+
u64 get_kaslr_seed(uintptr_t dtb_pa)
14+
{
15+
int node, len;
16+
fdt64_t *prop;
17+
u64 ret;
18+
19+
node = fdt_path_offset((void *)dtb_pa, "/chosen");
20+
if (node < 0)
21+
return 0;
22+
23+
prop = fdt_getprop_w((void *)dtb_pa, node, "kaslr-seed", &len);
24+
if (!prop || len != sizeof(u64))
25+
return 0;
26+
27+
ret = fdt64_to_cpu(*prop);
28+
*prop = 0;
29+
return ret;
30+
}

arch/riscv/kernel/setup.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <linux/smp.h>
2222
#include <linux/efi.h>
2323
#include <linux/crash_dump.h>
24+
#include <linux/panic_notifier.h>
2425

2526
#include <asm/acpi.h>
2627
#include <asm/alternative.h>
@@ -347,3 +348,27 @@ void free_initmem(void)
347348

348349
free_initmem_default(POISON_FREE_INITMEM);
349350
}
351+
352+
static int dump_kernel_offset(struct notifier_block *self,
353+
unsigned long v, void *p)
354+
{
355+
pr_emerg("Kernel Offset: 0x%lx from 0x%lx\n",
356+
kernel_map.virt_offset,
357+
KERNEL_LINK_ADDR);
358+
359+
return 0;
360+
}
361+
362+
static struct notifier_block kernel_offset_notifier = {
363+
.notifier_call = dump_kernel_offset
364+
};
365+
366+
static int __init register_kernel_offset_dumper(void)
367+
{
368+
if (IS_ENABLED(CONFIG_RANDOMIZE_BASE))
369+
atomic_notifier_chain_register(&panic_notifier_list,
370+
&kernel_offset_notifier);
371+
372+
return 0;
373+
}
374+
device_initcall(register_kernel_offset_dumper);

arch/riscv/mm/init.c

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1014,11 +1014,45 @@ static void __init pt_ops_set_late(void)
10141014
#endif
10151015
}
10161016

1017+
#ifdef CONFIG_RANDOMIZE_BASE
1018+
extern bool __init __pi_set_nokaslr_from_cmdline(uintptr_t dtb_pa);
1019+
extern u64 __init __pi_get_kaslr_seed(uintptr_t dtb_pa);
1020+
1021+
static int __init print_nokaslr(char *p)
1022+
{
1023+
pr_info("Disabled KASLR");
1024+
return 0;
1025+
}
1026+
early_param("nokaslr", print_nokaslr);
1027+
1028+
unsigned long kaslr_offset(void)
1029+
{
1030+
return kernel_map.virt_offset;
1031+
}
1032+
#endif
1033+
10171034
asmlinkage void __init setup_vm(uintptr_t dtb_pa)
10181035
{
10191036
pmd_t __maybe_unused fix_bmap_spmd, fix_bmap_epmd;
10201037

1021-
kernel_map.virt_addr = KERNEL_LINK_ADDR;
1038+
#ifdef CONFIG_RANDOMIZE_BASE
1039+
if (!__pi_set_nokaslr_from_cmdline(dtb_pa)) {
1040+
u64 kaslr_seed = __pi_get_kaslr_seed(dtb_pa);
1041+
u32 kernel_size = (uintptr_t)(&_end) - (uintptr_t)(&_start);
1042+
u32 nr_pos;
1043+
1044+
/*
1045+
* Compute the number of positions available: we are limited
1046+
* by the early page table that only has one PUD and we must
1047+
* be aligned on PMD_SIZE.
1048+
*/
1049+
nr_pos = (PUD_SIZE - kernel_size) / PMD_SIZE;
1050+
1051+
kernel_map.virt_offset = (kaslr_seed % nr_pos) * PMD_SIZE;
1052+
}
1053+
#endif
1054+
1055+
kernel_map.virt_addr = KERNEL_LINK_ADDR + kernel_map.virt_offset;
10221056
kernel_map.page_offset = _AC(CONFIG_PAGE_OFFSET, UL);
10231057

10241058
#ifdef CONFIG_XIP_KERNEL

0 commit comments

Comments
 (0)