aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteven Rostedt <srostedt@redhat.com>2009-02-17 17:57:30 -0500
committerSteven Rostedt <srostedt@redhat.com>2009-02-20 14:30:06 -0500
commit16239630974516a8879a3695ee9b4dc661f79f96 (patch)
tree366d00276e67b8b33db4367cca1717aafb3322b6
parent000ab691172db3921efa3cb7f17fc79235a1de7f (diff)
ftrace, x86: make kernel text writable only for conversions
Impact: keep kernel text read only Because dynamic ftrace converts the calls to mcount into and out of nops at run time, we needed to always keep the kernel text writable. But this defeats the point of CONFIG_DEBUG_RODATA. This patch converts the kernel code to writable before ftrace modifies the text, and converts it back to read only afterward. The kernel text is converted to read/write, stop_machine is called to modify the code, then the kernel text is converted back to read only. The original version used SYSTEM_STATE to determine when it was OK or not to change the code to rw or ro. Andrew Morton pointed out that using SYSTEM_STATE is a bad idea since there is no guarantee to what its state will actually be. Instead, I moved the check into the set_kernel_text_* functions themselves, and use a local variable to determine when it is OK to change the kernel text RW permissions. [ Update: Ingo Molnar suggested moving the prototypes to cacheflush.h ] Reviewed-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Steven Rostedt <srostedt@redhat.com>
-rw-r--r--arch/x86/include/asm/cacheflush.h5
-rw-r--r--arch/x86/kernel/ftrace.c13
-rw-r--r--arch/x86/mm/init_32.c35
-rw-r--r--arch/x86/mm/init_64.c37
4 files changed, 82 insertions, 8 deletions
diff --git a/arch/x86/include/asm/cacheflush.h b/arch/x86/include/asm/cacheflush.h
index 2f8466540fb5..6145063cfe0e 100644
--- a/arch/x86/include/asm/cacheflush.h
+++ b/arch/x86/include/asm/cacheflush.h
@@ -104,6 +104,11 @@ void clflush_cache_range(void *addr, unsigned int size);
104#ifdef CONFIG_DEBUG_RODATA 104#ifdef CONFIG_DEBUG_RODATA
105void mark_rodata_ro(void); 105void mark_rodata_ro(void);
106extern const int rodata_test_data; 106extern const int rodata_test_data;
107void set_kernel_text_rw(void);
108void set_kernel_text_ro(void);
109#else
110static inline void set_kernel_text_rw(void) { }
111static inline void set_kernel_text_ro(void) { }
107#endif 112#endif
108 113
109#ifdef CONFIG_DEBUG_RODATA_TEST 114#ifdef CONFIG_DEBUG_RODATA_TEST
diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c
index 231bdd3c5b1c..77857d4f7d0f 100644
--- a/arch/x86/kernel/ftrace.c
+++ b/arch/x86/kernel/ftrace.c
@@ -18,6 +18,7 @@
18#include <linux/init.h> 18#include <linux/init.h>
19#include <linux/list.h> 19#include <linux/list.h>
20 20
21#include <asm/cacheflush.h>
21#include <asm/ftrace.h> 22#include <asm/ftrace.h>
22#include <linux/ftrace.h> 23#include <linux/ftrace.h>
23#include <asm/nops.h> 24#include <asm/nops.h>
@@ -26,6 +27,18 @@
26 27
27#ifdef CONFIG_DYNAMIC_FTRACE 28#ifdef CONFIG_DYNAMIC_FTRACE
28 29
30int ftrace_arch_code_modify_prepare(void)
31{
32 set_kernel_text_rw();
33 return 0;
34}
35
36int ftrace_arch_code_modify_post_process(void)
37{
38 set_kernel_text_ro();
39 return 0;
40}
41
29union ftrace_code_union { 42union ftrace_code_union {
30 char code[MCOUNT_INSN_SIZE]; 43 char code[MCOUNT_INSN_SIZE];
31 struct { 44 struct {
diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c
index 2cef05074413..3eb2ed188a4c 100644
--- a/arch/x86/mm/init_32.c
+++ b/arch/x86/mm/init_32.c
@@ -1155,17 +1155,47 @@ static noinline int do_test_wp_bit(void)
1155const int rodata_test_data = 0xC3; 1155const int rodata_test_data = 0xC3;
1156EXPORT_SYMBOL_GPL(rodata_test_data); 1156EXPORT_SYMBOL_GPL(rodata_test_data);
1157 1157
1158static int kernel_set_to_readonly;
1159
1160void set_kernel_text_rw(void)
1161{
1162 unsigned long start = PFN_ALIGN(_text);
1163 unsigned long size = PFN_ALIGN(_etext) - start;
1164
1165 if (!kernel_set_to_readonly)
1166 return;
1167
1168 pr_debug("Set kernel text: %lx - %lx for read write\n",
1169 start, start+size);
1170
1171 set_pages_rw(virt_to_page(start), size >> PAGE_SHIFT);
1172}
1173
1174void set_kernel_text_ro(void)
1175{
1176 unsigned long start = PFN_ALIGN(_text);
1177 unsigned long size = PFN_ALIGN(_etext) - start;
1178
1179 if (!kernel_set_to_readonly)
1180 return;
1181
1182 pr_debug("Set kernel text: %lx - %lx for read only\n",
1183 start, start+size);
1184
1185 set_pages_ro(virt_to_page(start), size >> PAGE_SHIFT);
1186}
1187
1158void mark_rodata_ro(void) 1188void mark_rodata_ro(void)
1159{ 1189{
1160 unsigned long start = PFN_ALIGN(_text); 1190 unsigned long start = PFN_ALIGN(_text);
1161 unsigned long size = PFN_ALIGN(_etext) - start; 1191 unsigned long size = PFN_ALIGN(_etext) - start;
1162 1192
1163#ifndef CONFIG_DYNAMIC_FTRACE
1164 /* Dynamic tracing modifies the kernel text section */
1165 set_pages_ro(virt_to_page(start), size >> PAGE_SHIFT); 1193 set_pages_ro(virt_to_page(start), size >> PAGE_SHIFT);
1166 printk(KERN_INFO "Write protecting the kernel text: %luk\n", 1194 printk(KERN_INFO "Write protecting the kernel text: %luk\n",
1167 size >> 10); 1195 size >> 10);
1168 1196
1197 kernel_set_to_readonly = 1;
1198
1169#ifdef CONFIG_CPA_DEBUG 1199#ifdef CONFIG_CPA_DEBUG
1170 printk(KERN_INFO "Testing CPA: Reverting %lx-%lx\n", 1200 printk(KERN_INFO "Testing CPA: Reverting %lx-%lx\n",
1171 start, start+size); 1201 start, start+size);
@@ -1174,7 +1204,6 @@ void mark_rodata_ro(void)
1174 printk(KERN_INFO "Testing CPA: write protecting again\n"); 1204 printk(KERN_INFO "Testing CPA: write protecting again\n");
1175 set_pages_ro(virt_to_page(start), size>>PAGE_SHIFT); 1205 set_pages_ro(virt_to_page(start), size>>PAGE_SHIFT);
1176#endif 1206#endif
1177#endif /* CONFIG_DYNAMIC_FTRACE */
1178 1207
1179 start += size; 1208 start += size;
1180 size = (unsigned long)__end_rodata - start; 1209 size = (unsigned long)__end_rodata - start;
diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c
index e6d36b490250..63fdc531601d 100644
--- a/arch/x86/mm/init_64.c
+++ b/arch/x86/mm/init_64.c
@@ -986,21 +986,48 @@ void free_initmem(void)
986const int rodata_test_data = 0xC3; 986const int rodata_test_data = 0xC3;
987EXPORT_SYMBOL_GPL(rodata_test_data); 987EXPORT_SYMBOL_GPL(rodata_test_data);
988 988
989static int kernel_set_to_readonly;
990
991void set_kernel_text_rw(void)
992{
993 unsigned long start = PFN_ALIGN(_stext);
994 unsigned long end = PFN_ALIGN(__start_rodata);
995
996 if (!kernel_set_to_readonly)
997 return;
998
999 pr_debug("Set kernel text: %lx - %lx for read write\n",
1000 start, end);
1001
1002 set_memory_rw(start, (end - start) >> PAGE_SHIFT);
1003}
1004
1005void set_kernel_text_ro(void)
1006{
1007 unsigned long start = PFN_ALIGN(_stext);
1008 unsigned long end = PFN_ALIGN(__start_rodata);
1009
1010 if (!kernel_set_to_readonly)
1011 return;
1012
1013 pr_debug("Set kernel text: %lx - %lx for read only\n",
1014 start, end);
1015
1016 set_memory_ro(start, (end - start) >> PAGE_SHIFT);
1017}
1018
989void mark_rodata_ro(void) 1019void mark_rodata_ro(void)
990{ 1020{
991 unsigned long start = PFN_ALIGN(_stext), end = PFN_ALIGN(__end_rodata); 1021 unsigned long start = PFN_ALIGN(_stext), end = PFN_ALIGN(__end_rodata);
992 unsigned long rodata_start = 1022 unsigned long rodata_start =
993 ((unsigned long)__start_rodata + PAGE_SIZE - 1) & PAGE_MASK; 1023 ((unsigned long)__start_rodata + PAGE_SIZE - 1) & PAGE_MASK;
994 1024
995#ifdef CONFIG_DYNAMIC_FTRACE
996 /* Dynamic tracing modifies the kernel text section */
997 start = rodata_start;
998#endif
999
1000 printk(KERN_INFO "Write protecting the kernel read-only data: %luk\n", 1025 printk(KERN_INFO "Write protecting the kernel read-only data: %luk\n",
1001 (end - start) >> 10); 1026 (end - start) >> 10);
1002 set_memory_ro(start, (end - start) >> PAGE_SHIFT); 1027 set_memory_ro(start, (end - start) >> PAGE_SHIFT);
1003 1028
1029 kernel_set_to_readonly = 1;
1030
1004 /* 1031 /*
1005 * The rodata section (but not the kernel text!) should also be 1032 * The rodata section (but not the kernel text!) should also be
1006 * not-executable. 1033 * not-executable.