Skip to content

Commit 19a0086

Browse files
atishp04palmer-dabbelt
authored andcommitted
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: Atish Patra <[email protected]> Tested-by: Greentime Hu <[email protected]> Signed-off-by: Palmer Dabbelt <[email protected]>
1 parent b6566dc commit 19a0086

File tree

6 files changed

+67
-27
lines changed

6 files changed

+67
-27
lines changed

arch/riscv/include/asm/sections.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,7 @@
99

1010
extern char _start[];
1111
extern char _start_kernel[];
12+
extern char __init_data_begin[], __init_data_end[];
13+
extern char __init_text_begin[], __init_text_end[];
1214

1315
#endif /* __ASM_SECTIONS_H */

arch/riscv/include/asm/set_memory.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,15 @@ int set_memory_ro(unsigned long addr, int numpages);
1515
int set_memory_rw(unsigned long addr, int numpages);
1616
int set_memory_x(unsigned long addr, int numpages);
1717
int set_memory_nx(unsigned long addr, int numpages);
18+
int set_memory_rw_nx(unsigned long addr, int numpages);
19+
void protect_kernel_text_data(void);
1820
#else
1921
static inline int set_memory_ro(unsigned long addr, int numpages) { return 0; }
2022
static inline int set_memory_rw(unsigned long addr, int numpages) { return 0; }
2123
static inline int set_memory_x(unsigned long addr, int numpages) { return 0; }
2224
static inline int set_memory_nx(unsigned long addr, int numpages) { return 0; }
25+
static inline void protect_kernel_text_data(void) {};
26+
static inline int set_memory_rw_nx(unsigned long addr, int numpages) { return 0; }
2327
#endif
2428

2529
int set_direct_map_invalid_noflush(struct page *page);

arch/riscv/kernel/setup.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <asm/cpu_ops.h>
2525
#include <asm/early_ioremap.h>
2626
#include <asm/setup.h>
27+
#include <asm/set_memory.h>
2728
#include <asm/sections.h>
2829
#include <asm/sbi.h>
2930
#include <asm/tlbflush.h>
@@ -252,6 +253,8 @@ void __init setup_arch(char **cmdline_p)
252253
if (IS_ENABLED(CONFIG_RISCV_SBI))
253254
sbi_init();
254255

256+
if (IS_ENABLED(CONFIG_STRICT_KERNEL_RWX))
257+
protect_kernel_text_data();
255258
#ifdef CONFIG_SWIOTLB
256259
swiotlb_init(1);
257260
#endif
@@ -281,3 +284,12 @@ static int __init topology_init(void)
281284
return 0;
282285
}
283286
subsys_initcall(topology_init);
287+
288+
void free_initmem(void)
289+
{
290+
unsigned long init_begin = (unsigned long)__init_begin;
291+
unsigned long init_end = (unsigned long)__init_end;
292+
293+
set_memory_rw_nx(init_begin, (init_end - init_begin) >> PAGE_SHIFT);
294+
free_initmem_default(POISON_FREE_INITMEM);
295+
}

arch/riscv/kernel/vmlinux.lds.S

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,22 @@ SECTIONS
2929
HEAD_TEXT_SECTION
3030
. = ALIGN(PAGE_SIZE);
3131

32+
.text : {
33+
_text = .;
34+
_stext = .;
35+
TEXT_TEXT
36+
SCHED_TEXT
37+
CPUIDLE_TEXT
38+
LOCK_TEXT
39+
KPROBES_TEXT
40+
ENTRY_TEXT
41+
IRQENTRY_TEXT
42+
SOFTIRQENTRY_TEXT
43+
*(.fixup)
44+
_etext = .;
45+
}
46+
47+
. = ALIGN(SECTION_ALIGN);
3248
__init_begin = .;
3349
__init_text_begin = .;
3450
.init.text : AT(ADDR(.init.text) - LOAD_OFFSET) ALIGN(SECTION_ALIGN) { \
@@ -53,35 +69,24 @@ SECTIONS
5369
{
5470
EXIT_TEXT
5571
}
56-
.exit.data :
57-
{
58-
EXIT_DATA
59-
}
60-
PERCPU_SECTION(L1_CACHE_BYTES)
61-
__init_end = .;
6272

73+
__init_text_end = .;
6374
. = ALIGN(SECTION_ALIGN);
64-
.text : {
65-
_text = .;
66-
_stext = .;
67-
TEXT_TEXT
68-
SCHED_TEXT
69-
CPUIDLE_TEXT
70-
LOCK_TEXT
71-
KPROBES_TEXT
72-
ENTRY_TEXT
73-
IRQENTRY_TEXT
74-
SOFTIRQENTRY_TEXT
75-
*(.fixup)
76-
_etext = .;
77-
}
78-
7975
#ifdef CONFIG_EFI
8076
. = ALIGN(PECOFF_SECTION_ALIGNMENT);
8177
__pecoff_text_end = .;
8278
#endif
83-
79+
/* Start of init data section */
80+
__init_data_begin = .;
8481
INIT_DATA_SECTION(16)
82+
.exit.data :
83+
{
84+
EXIT_DATA
85+
}
86+
PERCPU_SECTION(L1_CACHE_BYTES)
87+
88+
__init_data_end = .;
89+
__init_end = .;
8590

8691
/* Start of data section */
8792
_sdata = .;

arch/riscv/mm/init.c

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -612,18 +612,29 @@ static inline void setup_vm_final(void)
612612
#endif /* CONFIG_MMU */
613613

614614
#ifdef CONFIG_STRICT_KERNEL_RWX
615-
void mark_rodata_ro(void)
615+
void protect_kernel_text_data(void)
616616
{
617-
unsigned long text_start = (unsigned long)_text;
618-
unsigned long text_end = (unsigned long)_etext;
617+
unsigned long text_start = (unsigned long)_start;
618+
unsigned long init_text_start = (unsigned long)__init_text_begin;
619+
unsigned long init_data_start = (unsigned long)__init_data_begin;
619620
unsigned long rodata_start = (unsigned long)__start_rodata;
620621
unsigned long data_start = (unsigned long)_data;
621622
unsigned long max_low = (unsigned long)(__va(PFN_PHYS(max_low_pfn)));
622623

623-
set_memory_ro(text_start, (text_end - text_start) >> PAGE_SHIFT);
624-
set_memory_ro(rodata_start, (data_start - rodata_start) >> PAGE_SHIFT);
624+
set_memory_ro(text_start, (init_text_start - text_start) >> PAGE_SHIFT);
625+
set_memory_ro(init_text_start, (init_data_start - init_text_start) >> PAGE_SHIFT);
626+
set_memory_nx(init_data_start, (rodata_start - init_data_start) >> PAGE_SHIFT);
627+
/* rodata section is marked readonly in mark_rodata_ro */
625628
set_memory_nx(rodata_start, (data_start - rodata_start) >> PAGE_SHIFT);
626629
set_memory_nx(data_start, (max_low - data_start) >> PAGE_SHIFT);
630+
}
631+
632+
void mark_rodata_ro(void)
633+
{
634+
unsigned long rodata_start = (unsigned long)__start_rodata;
635+
unsigned long data_start = (unsigned long)_data;
636+
637+
set_memory_ro(rodata_start, (data_start - rodata_start) >> PAGE_SHIFT);
627638

628639
debug_checkwx();
629640
}

arch/riscv/mm/pageattr.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,12 @@ static int __set_memory(unsigned long addr, int numpages, pgprot_t set_mask,
128128
return ret;
129129
}
130130

131+
int set_memory_rw_nx(unsigned long addr, int numpages)
132+
{
133+
return __set_memory(addr, numpages, __pgprot(_PAGE_READ | _PAGE_WRITE),
134+
__pgprot(_PAGE_EXEC));
135+
}
136+
131137
int set_memory_ro(unsigned long addr, int numpages)
132138
{
133139
return __set_memory(addr, numpages, __pgprot(_PAGE_READ),

0 commit comments

Comments
 (0)