aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKees Cook <keescook@chromium.org>2014-04-03 16:29:50 -0400
committerKees Cook <keescook@chromium.org>2014-10-16 17:38:54 -0400
commit80d6b0c2eed2a504f6740cd1f5ea76dc50abfc4d (patch)
tree32f6d8a1c5a2250cc3f303df545dfbf52da62d19
parent1e6b48116a95046ec51f3d40f83aff8b006674d7 (diff)
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 <keescook@chromium.org> Tested-by: Laura Abbott <lauraa@codeaurora.org> Acked-by: Nicolas Pitre <nico@linaro.org>
-rw-r--r--arch/arm/include/asm/cacheflush.h10
-rw-r--r--arch/arm/kernel/ftrace.c19
-rw-r--r--arch/arm/kernel/machine_kexec.c1
-rw-r--r--arch/arm/kernel/vmlinux.lds.S3
-rw-r--r--arch/arm/mm/Kconfig12
-rw-r--r--arch/arm/mm/init.c48
6 files changed, 92 insertions, 1 deletions
diff --git a/arch/arm/include/asm/cacheflush.h b/arch/arm/include/asm/cacheflush.h
index 10e78d00a0bb..2d46862e7bef 100644
--- a/arch/arm/include/asm/cacheflush.h
+++ b/arch/arm/include/asm/cacheflush.h
@@ -487,6 +487,16 @@ int set_memory_rw(unsigned long addr, int numpages);
487int set_memory_x(unsigned long addr, int numpages); 487int set_memory_x(unsigned long addr, int numpages);
488int set_memory_nx(unsigned long addr, int numpages); 488int set_memory_nx(unsigned long addr, int numpages);
489 489
490#ifdef CONFIG_DEBUG_RODATA
491void mark_rodata_ro(void);
492void set_kernel_text_rw(void);
493void set_kernel_text_ro(void);
494#else
495static inline void set_kernel_text_rw(void) { }
496static inline void set_kernel_text_ro(void) { }
497#endif
498
490void flush_uprobe_xol_access(struct page *page, unsigned long uaddr, 499void flush_uprobe_xol_access(struct page *page, unsigned long uaddr,
491 void *kaddr, unsigned long len); 500 void *kaddr, unsigned long len);
501
492#endif 502#endif
diff --git a/arch/arm/kernel/ftrace.c b/arch/arm/kernel/ftrace.c
index af9a8a927a4e..b8c75e45a950 100644
--- a/arch/arm/kernel/ftrace.c
+++ b/arch/arm/kernel/ftrace.c
@@ -15,6 +15,7 @@
15#include <linux/ftrace.h> 15#include <linux/ftrace.h>
16#include <linux/uaccess.h> 16#include <linux/uaccess.h>
17#include <linux/module.h> 17#include <linux/module.h>
18#include <linux/stop_machine.h>
18 19
19#include <asm/cacheflush.h> 20#include <asm/cacheflush.h>
20#include <asm/opcodes.h> 21#include <asm/opcodes.h>
@@ -35,6 +36,22 @@
35 36
36#define OLD_NOP 0xe1a00000 /* mov r0, r0 */ 37#define OLD_NOP 0xe1a00000 /* mov r0, r0 */
37 38
39static int __ftrace_modify_code(void *data)
40{
41 int *command = data;
42
43 set_kernel_text_rw();
44 ftrace_modify_all_code(*command);
45 set_kernel_text_ro();
46
47 return 0;
48}
49
50void arch_ftrace_update_code(int command)
51{
52 stop_machine(__ftrace_modify_code, &command, NULL);
53}
54
38static unsigned long ftrace_nop_replace(struct dyn_ftrace *rec) 55static unsigned long ftrace_nop_replace(struct dyn_ftrace *rec)
39{ 56{
40 return rec->arch.old_mcount ? OLD_NOP : NOP; 57 return rec->arch.old_mcount ? OLD_NOP : NOP;
@@ -73,6 +90,8 @@ int ftrace_arch_code_modify_prepare(void)
73int ftrace_arch_code_modify_post_process(void) 90int ftrace_arch_code_modify_post_process(void)
74{ 91{
75 set_all_modules_text_ro(); 92 set_all_modules_text_ro();
93 /* Make sure any TLB misses during machine stop are cleared. */
94 flush_tlb_all();
76 return 0; 95 return 0;
77} 96}
78 97
diff --git a/arch/arm/kernel/machine_kexec.c b/arch/arm/kernel/machine_kexec.c
index 8f75250cbe30..4423a565ef6f 100644
--- a/arch/arm/kernel/machine_kexec.c
+++ b/arch/arm/kernel/machine_kexec.c
@@ -164,6 +164,7 @@ void machine_kexec(struct kimage *image)
164 reboot_code_buffer = page_address(image->control_code_page); 164 reboot_code_buffer = page_address(image->control_code_page);
165 165
166 /* Prepare parameters for reboot_code_buffer*/ 166 /* Prepare parameters for reboot_code_buffer*/
167 set_kernel_text_rw();
167 kexec_start_address = image->start; 168 kexec_start_address = image->start;
168 kexec_indirection_page = page_list; 169 kexec_indirection_page = page_list;
169 kexec_mach_type = machine_arch_type; 170 kexec_mach_type = machine_arch_type;
diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S
index 18fd68a295ea..3afcb6c2cf06 100644
--- a/arch/arm/kernel/vmlinux.lds.S
+++ b/arch/arm/kernel/vmlinux.lds.S
@@ -120,6 +120,9 @@ SECTIONS
120 ARM_CPU_KEEP(PROC_INFO) 120 ARM_CPU_KEEP(PROC_INFO)
121 } 121 }
122 122
123#ifdef CONFIG_DEBUG_RODATA
124 . = ALIGN(1<<SECTION_SHIFT);
125#endif
123 RO_DATA(PAGE_SIZE) 126 RO_DATA(PAGE_SIZE)
124 127
125 . = ALIGN(4); 128 . = ALIGN(4);
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
1017 padded to section-size (1MiB) boundaries (because their permissions 1017 padded to section-size (1MiB) boundaries (because their permissions
1018 are different and splitting the 1M pages into 4K ones causes TLB 1018 are different and splitting the 1M pages into 4K ones causes TLB
1019 performance problems), wasting memory. 1019 performance problems), wasting memory.
1020
1021config DEBUG_RODATA
1022 bool "Make kernel text and rodata read-only"
1023 depends on ARM_KERNMEM_PERMS
1024 default y
1025 help
1026 If this is set, kernel text and rodata will be made read-only. This
1027 is to help catch accidental or malicious attempts to change the
1028 kernel's executable code. Additionally splits rodata from kernel
1029 text so it can be made explicitly non-executable. This creates
1030 another section-size padded region, so it can waste more memory
1031 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 {
622 unsigned long end; 622 unsigned long end;
623 pmdval_t mask; 623 pmdval_t mask;
624 pmdval_t prot; 624 pmdval_t prot;
625 pmdval_t clear;
625}; 626};
626 627
627struct section_perm nx_perms[] = { 628static struct section_perm nx_perms[] = {
628 /* Make pages tables, etc before _stext RW (set NX). */ 629 /* Make pages tables, etc before _stext RW (set NX). */
629 { 630 {
630 .start = PAGE_OFFSET, 631 .start = PAGE_OFFSET,
@@ -639,8 +640,35 @@ struct section_perm nx_perms[] = {
639 .mask = ~PMD_SECT_XN, 640 .mask = ~PMD_SECT_XN,
640 .prot = PMD_SECT_XN, 641 .prot = PMD_SECT_XN,
641 }, 642 },
643#ifdef CONFIG_DEBUG_RODATA
644 /* Make rodata NX (set RO in ro_perms below). */
645 {
646 .start = (unsigned long)__start_rodata,
647 .end = (unsigned long)__init_begin,
648 .mask = ~PMD_SECT_XN,
649 .prot = PMD_SECT_XN,
650 },
651#endif
642}; 652};
643 653
654#ifdef CONFIG_DEBUG_RODATA
655static struct section_perm ro_perms[] = {
656 /* Make kernel code and rodata RX (set RO). */
657 {
658 .start = (unsigned long)_stext,
659 .end = (unsigned long)__init_begin,
660#ifdef CONFIG_ARM_LPAE
661 .mask = ~PMD_SECT_RDONLY,
662 .prot = PMD_SECT_RDONLY,
663#else
664 .mask = ~(PMD_SECT_APX | PMD_SECT_AP_WRITE),
665 .prot = PMD_SECT_APX | PMD_SECT_AP_WRITE,
666 .clear = PMD_SECT_AP_WRITE,
667#endif
668 },
669};
670#endif
671
644/* 672/*
645 * Updates section permissions only for the current mm (sections are 673 * Updates section permissions only for the current mm (sections are
646 * copied into each mm). During startup, this is the init_mm. Is only 674 * copied into each mm). During startup, this is the init_mm. Is only
@@ -704,6 +732,24 @@ static inline void fix_kernmem_perms(void)
704{ 732{
705 set_section_perms(nx_perms, prot); 733 set_section_perms(nx_perms, prot);
706} 734}
735
736#ifdef CONFIG_DEBUG_RODATA
737void mark_rodata_ro(void)
738{
739 set_section_perms(ro_perms, prot);
740}
741
742void set_kernel_text_rw(void)
743{
744 set_section_perms(ro_perms, clear);
745}
746
747void set_kernel_text_ro(void)
748{
749 set_section_perms(ro_perms, prot);
750}
751#endif /* CONFIG_DEBUG_RODATA */
752
707#else 753#else
708static inline void fix_kernmem_perms(void) { } 754static inline void fix_kernmem_perms(void) { }
709#endif /* CONFIG_ARM_KERNMEM_PERMS */ 755#endif /* CONFIG_ARM_KERNMEM_PERMS */