Unverified Commit 19a00869 authored by Atish Patra's avatar Atish Patra Committed by Palmer Dabbelt
Browse files

RISC-V: Protect all kernel sections including init early



Currently, .init.text & .init.data are intermixed which makes it impossible
apply different permissions to them. .init.data shouldn't need exec
permissions while .init.text shouldn't have write permission. Moreover,
the strict permission are only enforced /init starts. This leaves the
kernel vulnerable from possible buggy built-in modules.

Keep .init.text & .data in separate sections so that different permissions
are applied to each section. Apply permissions to individual sections as
early as possible. This improves the kernel protection under
CONFIG_STRICT_KERNEL_RWX. We also need to restore the permissions for the
entire _init section after it is freed so that those pages can be used
for other purpose.

Signed-off-by: default avatarAtish Patra <atish.patra@wdc.com>
Tested-by: default avatarGreentime Hu <greentime.hu@sifive.com>
Signed-off-by: default avatarPalmer Dabbelt <palmerdabbelt@google.com>
parent b6566dc1
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -9,5 +9,7 @@

extern char _start[];
extern char _start_kernel[];
extern char __init_data_begin[], __init_data_end[];
extern char __init_text_begin[], __init_text_end[];

#endif /* __ASM_SECTIONS_H */
+4 −0
Original line number Diff line number Diff line
@@ -15,11 +15,15 @@ int set_memory_ro(unsigned long addr, int numpages);
int set_memory_rw(unsigned long addr, int numpages);
int set_memory_x(unsigned long addr, int numpages);
int set_memory_nx(unsigned long addr, int numpages);
int set_memory_rw_nx(unsigned long addr, int numpages);
void protect_kernel_text_data(void);
#else
static inline int set_memory_ro(unsigned long addr, int numpages) { return 0; }
static inline int set_memory_rw(unsigned long addr, int numpages) { return 0; }
static inline int set_memory_x(unsigned long addr, int numpages) { return 0; }
static inline int set_memory_nx(unsigned long addr, int numpages) { return 0; }
static inline void protect_kernel_text_data(void) {};
static inline int set_memory_rw_nx(unsigned long addr, int numpages) { return 0; }
#endif

int set_direct_map_invalid_noflush(struct page *page);
+12 −0
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@
#include <asm/cpu_ops.h>
#include <asm/early_ioremap.h>
#include <asm/setup.h>
#include <asm/set_memory.h>
#include <asm/sections.h>
#include <asm/sbi.h>
#include <asm/tlbflush.h>
@@ -252,6 +253,8 @@ void __init setup_arch(char **cmdline_p)
	if (IS_ENABLED(CONFIG_RISCV_SBI))
		sbi_init();

	if (IS_ENABLED(CONFIG_STRICT_KERNEL_RWX))
		protect_kernel_text_data();
#ifdef CONFIG_SWIOTLB
	swiotlb_init(1);
#endif
@@ -281,3 +284,12 @@ static int __init topology_init(void)
	return 0;
}
subsys_initcall(topology_init);

void free_initmem(void)
{
	unsigned long init_begin = (unsigned long)__init_begin;
	unsigned long init_end = (unsigned long)__init_end;

	set_memory_rw_nx(init_begin, (init_end - init_begin) >> PAGE_SHIFT);
	free_initmem_default(POISON_FREE_INITMEM);
}
+27 −22
Original line number Diff line number Diff line
@@ -29,6 +29,22 @@ SECTIONS
	HEAD_TEXT_SECTION
	. = ALIGN(PAGE_SIZE);

	.text : {
		_text = .;
		_stext = .;
		TEXT_TEXT
		SCHED_TEXT
		CPUIDLE_TEXT
		LOCK_TEXT
		KPROBES_TEXT
		ENTRY_TEXT
		IRQENTRY_TEXT
		SOFTIRQENTRY_TEXT
		*(.fixup)
		_etext = .;
	}

	. = ALIGN(SECTION_ALIGN);
	__init_begin = .;
	__init_text_begin = .;
	.init.text : AT(ADDR(.init.text) - LOAD_OFFSET) ALIGN(SECTION_ALIGN) { \
@@ -53,35 +69,24 @@ SECTIONS
	{
		EXIT_TEXT
	}
	.exit.data :
	{
		EXIT_DATA
	}
	PERCPU_SECTION(L1_CACHE_BYTES)
	__init_end = .;

	__init_text_end = .;
	. = ALIGN(SECTION_ALIGN);
	.text : {
		_text = .;
		_stext = .;
		TEXT_TEXT
		SCHED_TEXT
		CPUIDLE_TEXT
		LOCK_TEXT
		KPROBES_TEXT
		ENTRY_TEXT
		IRQENTRY_TEXT
		SOFTIRQENTRY_TEXT
		*(.fixup)
		_etext = .;
	}

#ifdef CONFIG_EFI
	. = ALIGN(PECOFF_SECTION_ALIGNMENT);
	__pecoff_text_end = .;
#endif

	/* Start of init data section */
	__init_data_begin = .;
	INIT_DATA_SECTION(16)
	.exit.data :
	{
		EXIT_DATA
	}
	PERCPU_SECTION(L1_CACHE_BYTES)

	__init_data_end = .;
	__init_end = .;

	/* Start of data section */
	_sdata = .;
+16 −5
Original line number Diff line number Diff line
@@ -612,18 +612,29 @@ static inline void setup_vm_final(void)
#endif /* CONFIG_MMU */

#ifdef CONFIG_STRICT_KERNEL_RWX
void mark_rodata_ro(void)
void protect_kernel_text_data(void)
{
	unsigned long text_start = (unsigned long)_text;
	unsigned long text_end = (unsigned long)_etext;
	unsigned long text_start = (unsigned long)_start;
	unsigned long init_text_start = (unsigned long)__init_text_begin;
	unsigned long init_data_start = (unsigned long)__init_data_begin;
	unsigned long rodata_start = (unsigned long)__start_rodata;
	unsigned long data_start = (unsigned long)_data;
	unsigned long max_low = (unsigned long)(__va(PFN_PHYS(max_low_pfn)));

	set_memory_ro(text_start, (text_end - text_start) >> PAGE_SHIFT);
	set_memory_ro(rodata_start, (data_start - rodata_start) >> PAGE_SHIFT);
	set_memory_ro(text_start, (init_text_start - text_start) >> PAGE_SHIFT);
	set_memory_ro(init_text_start, (init_data_start - init_text_start) >> PAGE_SHIFT);
	set_memory_nx(init_data_start, (rodata_start - init_data_start) >> PAGE_SHIFT);
	/* rodata section is marked readonly in mark_rodata_ro */
	set_memory_nx(rodata_start, (data_start - rodata_start) >> PAGE_SHIFT);
	set_memory_nx(data_start, (max_low - data_start) >> PAGE_SHIFT);
}

void mark_rodata_ro(void)
{
	unsigned long rodata_start = (unsigned long)__start_rodata;
	unsigned long data_start = (unsigned long)_data;

	set_memory_ro(rodata_start, (data_start - rodata_start) >> PAGE_SHIFT);

	debug_checkwx();
}
Loading