From 80d6b0c2eed2a504f6740cd1f5ea76dc50abfc4d Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 3 Apr 2014 13:29:50 -0700 Subject: ARM: mm: allow text and rodata sections to be read-only This introduces CONFIG_DEBUG_RODATA, making kernel text and rodata read-only. Additionally, this splits rodata from text so that rodata can also be NX, which may lead to wasted memory when aligning to SECTION_SIZE. The read-only areas are made writable during ftrace updates and kexec. Signed-off-by: Kees Cook Tested-by: Laura Abbott Acked-by: Nicolas Pitre --- arch/arm/mm/Kconfig | 12 ++++++++++++ arch/arm/mm/init.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 59 insertions(+), 1 deletion(-) (limited to 'arch/arm/mm') diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig index 7a0756df91a2..c9cd9c5bf1e1 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig @@ -1017,3 +1017,15 @@ config ARM_KERNMEM_PERMS padded to section-size (1MiB) boundaries (because their permissions are different and splitting the 1M pages into 4K ones causes TLB performance problems), wasting memory. + +config DEBUG_RODATA + bool "Make kernel text and rodata read-only" + depends on ARM_KERNMEM_PERMS + default y + help + If this is set, kernel text and rodata will be made read-only. This + is to help catch accidental or malicious attempts to change the + kernel's executable code. Additionally splits rodata from kernel + text so it can be made explicitly non-executable. This creates + another section-size padded region, so it can waste more memory + space while gaining the read-only protections. diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index e6bfe76b2f59..dc2db779cdf4 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -622,9 +622,10 @@ struct section_perm { unsigned long end; pmdval_t mask; pmdval_t prot; + pmdval_t clear; }; -struct section_perm nx_perms[] = { +static struct section_perm nx_perms[] = { /* Make pages tables, etc before _stext RW (set NX). */ { .start = PAGE_OFFSET, @@ -639,8 +640,35 @@ struct section_perm nx_perms[] = { .mask = ~PMD_SECT_XN, .prot = PMD_SECT_XN, }, +#ifdef CONFIG_DEBUG_RODATA + /* Make rodata NX (set RO in ro_perms below). */ + { + .start = (unsigned long)__start_rodata, + .end = (unsigned long)__init_begin, + .mask = ~PMD_SECT_XN, + .prot = PMD_SECT_XN, + }, +#endif }; +#ifdef CONFIG_DEBUG_RODATA +static struct section_perm ro_perms[] = { + /* Make kernel code and rodata RX (set RO). */ + { + .start = (unsigned long)_stext, + .end = (unsigned long)__init_begin, +#ifdef CONFIG_ARM_LPAE + .mask = ~PMD_SECT_RDONLY, + .prot = PMD_SECT_RDONLY, +#else + .mask = ~(PMD_SECT_APX | PMD_SECT_AP_WRITE), + .prot = PMD_SECT_APX | PMD_SECT_AP_WRITE, + .clear = PMD_SECT_AP_WRITE, +#endif + }, +}; +#endif + /* * Updates section permissions only for the current mm (sections are * copied into each mm). During startup, this is the init_mm. Is only @@ -704,6 +732,24 @@ static inline void fix_kernmem_perms(void) { set_section_perms(nx_perms, prot); } + +#ifdef CONFIG_DEBUG_RODATA +void mark_rodata_ro(void) +{ + set_section_perms(ro_perms, prot); +} + +void set_kernel_text_rw(void) +{ + set_section_perms(ro_perms, clear); +} + +void set_kernel_text_ro(void) +{ + set_section_perms(ro_perms, prot); +} +#endif /* CONFIG_DEBUG_RODATA */ + #else static inline void fix_kernmem_perms(void) { } #endif /* CONFIG_ARM_KERNMEM_PERMS */ -- cgit v1.2.3