Commit edeed30589f5defe63ce6aaae56f2b7c855e4520
Committed by
Ingo Molnar
1 parent
adafdf6a4e
Exists in
master
and in
7 other branches
x86: add testcases for RODATA and NX protections/attributes
Latest update; I now have 4 NX tests, but 2 fail so they're #if 0'd. I also cleaned up the NX test code quite a bit, and got rid of the ugly exception table sorting stuff. From: Arjan van de Ven <arjan@linux.intel.com> This patch adds testcases for the CONFIG_DEBUG_RODATA configuration option as well as the NX CPU feature/mappings. Both testcases can move to tests/ once that patch gets merged into mainline. (I'm half considering moving the rodata test into mm/init.c but I'll wait with that until init.c is unified) As part of this I had to fix a not-quite-right alignment in the vmlinux.lds.h for the RODATA sections, which lead to 1 page less being marked read only. Signed-off-by: Arjan van de Ven <arjan@linux.intel.com> Signed-off-by: Ingo Molnar <mingo@elte.hu> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Showing 6 changed files with 276 additions and 0 deletions Side-by-side Diff
arch/x86/kernel/test_nx.c
1 | +/* | |
2 | + * test_nx.c: functional test for NX functionality | |
3 | + * | |
4 | + * (C) Copyright 2008 Intel Corporation | |
5 | + * Author: Arjan van de Ven <arjan@linux.intel.com> | |
6 | + * | |
7 | + * This program is free software; you can redistribute it and/or | |
8 | + * modify it under the terms of the GNU General Public License | |
9 | + * as published by the Free Software Foundation; version 2 | |
10 | + * of the License. | |
11 | + */ | |
12 | +#include <linux/module.h> | |
13 | +#include <linux/sort.h> | |
14 | +#include <asm/uaccess.h> | |
15 | + | |
16 | +extern int rodata_test_data; | |
17 | + | |
18 | +/* | |
19 | + * This file checks 4 things: | |
20 | + * 1) Check if the stack is not executable | |
21 | + * 2) Check if kmalloc memory is not executable | |
22 | + * 3) Check if the .rodata section is not executable | |
23 | + * 4) Check if the .data section of a module is not executable | |
24 | + * | |
25 | + * To do this, the test code tries to execute memory in stack/kmalloc/etc, | |
26 | + * and then checks if the expected trap happens. | |
27 | + * | |
28 | + * Sadly, this implies having a dynamic exception handling table entry. | |
29 | + * ... which can be done (and will make Rusty cry)... but it can only | |
30 | + * be done in a stand-alone module with only 1 entry total. | |
31 | + * (otherwise we'd have to sort and that's just too messy) | |
32 | + */ | |
33 | + | |
34 | + | |
35 | + | |
36 | +/* | |
37 | + * We want to set up an exception handling point on our stack, | |
38 | + * which means a variable value. This function is rather dirty | |
39 | + * and walks the exception table of the module, looking for a magic | |
40 | + * marker and replaces it with a specific function. | |
41 | + */ | |
42 | +static void fudze_exception_table(void *marker, void *new) | |
43 | +{ | |
44 | + struct module *mod = THIS_MODULE; | |
45 | + struct exception_table_entry *extable; | |
46 | + | |
47 | + /* | |
48 | + * Note: This module has only 1 exception table entry, | |
49 | + * so searching and sorting is not needed. If that changes, | |
50 | + * this would be the place to search and re-sort the exception | |
51 | + * table. | |
52 | + */ | |
53 | + if (mod->num_exentries > 1) { | |
54 | + printk(KERN_ERR "test_nx: too many exception table entries!\n"); | |
55 | + printk(KERN_ERR "test_nx: test results are not reliable.\n"); | |
56 | + return; | |
57 | + } | |
58 | + extable = (struct exception_table_entry *)mod->extable; | |
59 | + extable[0].insn = (unsigned long)new; | |
60 | +} | |
61 | + | |
62 | + | |
63 | +/* | |
64 | + * exception tables get their symbols translated so we need | |
65 | + * to use a fake function to put in there, which we can then | |
66 | + * replace at runtime. | |
67 | + */ | |
68 | +void foo_label(void); | |
69 | + | |
70 | +/* | |
71 | + * returns 0 for not-executable, negative for executable | |
72 | + * | |
73 | + * Note: we cannot allow this function to be inlined, because | |
74 | + * that would give us more than 1 exception table entry. | |
75 | + * This in turn would break the assumptions above. | |
76 | + */ | |
77 | +static noinline int test_address(void *address) | |
78 | +{ | |
79 | + unsigned long result; | |
80 | + | |
81 | + /* Set up an exception table entry for our address */ | |
82 | + fudze_exception_table(&foo_label, address); | |
83 | + result = 1; | |
84 | + asm volatile( | |
85 | + "foo_label:\n" | |
86 | + "0: call *%[fake_code]\n" | |
87 | + "1:\n" | |
88 | + ".section .fixup,\"ax\"\n" | |
89 | + "2: mov %[zero], %[rslt]\n" | |
90 | + " ret\n" | |
91 | + ".previous\n" | |
92 | + ".section __ex_table,\"a\"\n" | |
93 | + " .align 8\n" | |
94 | + " .quad 0b\n" | |
95 | + " .quad 2b\n" | |
96 | + ".previous\n" | |
97 | + : [rslt] "=r" (result) | |
98 | + : [fake_code] "r" (address), [zero] "r" (0UL), "0" (result) | |
99 | + ); | |
100 | + /* change the exception table back for the next round */ | |
101 | + fudze_exception_table(address, &foo_label); | |
102 | + | |
103 | + if (result) | |
104 | + return -ENODEV; | |
105 | + return 0; | |
106 | +} | |
107 | + | |
108 | +static unsigned char test_data = 0xC3; /* 0xC3 is the opcode for "ret" */ | |
109 | + | |
110 | +static int test_NX(void) | |
111 | +{ | |
112 | + int ret = 0; | |
113 | + /* 0xC3 is the opcode for "ret" */ | |
114 | + char stackcode[] = {0xC3, 0x90, 0 }; | |
115 | + char *heap; | |
116 | + | |
117 | + test_data = 0xC3; | |
118 | + | |
119 | + printk(KERN_INFO "Testing NX protection\n"); | |
120 | + | |
121 | + /* Test 1: check if the stack is not executable */ | |
122 | + if (test_address(&stackcode)) { | |
123 | + printk(KERN_ERR "test_nx: stack was executable\n"); | |
124 | + ret = -ENODEV; | |
125 | + } | |
126 | + | |
127 | + | |
128 | + /* Test 2: Check if the heap is executable */ | |
129 | + heap = kmalloc(64, GFP_KERNEL); | |
130 | + if (!heap) | |
131 | + return -ENOMEM; | |
132 | + heap[0] = 0xC3; /* opcode for "ret" */ | |
133 | + | |
134 | + if (test_address(heap)) { | |
135 | + printk(KERN_ERR "test_nx: heap was executable\n"); | |
136 | + ret = -ENODEV; | |
137 | + } | |
138 | + kfree(heap); | |
139 | + | |
140 | + /* | |
141 | + * The following 2 tests currently fail, this needs to get fixed | |
142 | + * Until then, don't run them to avoid too many people getting scared | |
143 | + * by the error message | |
144 | + */ | |
145 | +#if 0 | |
146 | + | |
147 | +#ifdef CONFIG_DEBUG_RODATA | |
148 | + /* Test 3: Check if the .rodata section is executable */ | |
149 | + if (rodata_test_data != 0xC3) { | |
150 | + printk(KERN_ERR "test_nx: .rodata marker has invalid value\n"); | |
151 | + ret = -ENODEV; | |
152 | + } else if (test_address(&rodata_test_data)) { | |
153 | + printk(KERN_ERR "test_nx: .rodata section is executable\n"); | |
154 | + ret = -ENODEV; | |
155 | + } | |
156 | +#endif | |
157 | + | |
158 | + /* Test 4: Check if the .data section of a module is executable */ | |
159 | + if (test_address(&test_data)) { | |
160 | + printk(KERN_ERR "test_nx: .data section is executable\n"); | |
161 | + ret = -ENODEV; | |
162 | + } | |
163 | + | |
164 | +#endif | |
165 | + return 0; | |
166 | +} | |
167 | + | |
168 | +static void test_exit(void) | |
169 | +{ | |
170 | +} | |
171 | + | |
172 | +module_init(test_NX); | |
173 | +module_exit(test_exit); | |
174 | +MODULE_LICENSE("GPL"); | |
175 | +MODULE_DESCRIPTION("Testcase for the NX infrastructure"); | |
176 | +MODULE_AUTHOR("Arjan van de Ven <arjan@linux.intel.com>"); |
arch/x86/kernel/test_rodata.c
1 | +/* | |
2 | + * test_rodata.c: functional test for mark_rodata_ro function | |
3 | + * | |
4 | + * (C) Copyright 2008 Intel Corporation | |
5 | + * Author: Arjan van de Ven <arjan@linux.intel.com> | |
6 | + * | |
7 | + * This program is free software; you can redistribute it and/or | |
8 | + * modify it under the terms of the GNU General Public License | |
9 | + * as published by the Free Software Foundation; version 2 | |
10 | + * of the License. | |
11 | + */ | |
12 | +#include <linux/module.h> | |
13 | +#include <asm/sections.h> | |
14 | +extern int rodata_test_data; | |
15 | + | |
16 | +int rodata_test(void) | |
17 | +{ | |
18 | + unsigned long result; | |
19 | + unsigned long start, end; | |
20 | + | |
21 | + /* test 1: read the value */ | |
22 | + /* If this test fails, some previous testrun has clobbered the state */ | |
23 | + if (!rodata_test_data) { | |
24 | + printk(KERN_ERR "rodata_test: test 1 fails (start data)\n"); | |
25 | + return -ENODEV; | |
26 | + } | |
27 | + | |
28 | + /* test 2: write to the variable; this should fault */ | |
29 | + /* | |
30 | + * If this test fails, we managed to overwrite the data | |
31 | + * | |
32 | + * This is written in assembly to be able to catch the | |
33 | + * exception that is supposed to happen in the correct | |
34 | + * case | |
35 | + */ | |
36 | + | |
37 | + result = 1; | |
38 | + asm volatile( | |
39 | + "0: mov %[zero],(%[rodata_test])\n" | |
40 | + " mov %[zero], %[rslt]\n" | |
41 | + "1:\n" | |
42 | + ".section .fixup,\"ax\"\n" | |
43 | + "2: jmp 1b\n" | |
44 | + ".previous\n" | |
45 | + ".section __ex_table,\"a\"\n" | |
46 | + " .align 16\n" | |
47 | +#ifdef CONFIG_X86_32 | |
48 | + " .long 0b,2b\n" | |
49 | +#else | |
50 | + " .quad 0b,2b\n" | |
51 | +#endif | |
52 | + ".previous" | |
53 | + : [rslt] "=r" (result) | |
54 | + : [rodata_test] "r" (&rodata_test_data), [zero] "r" (0UL) | |
55 | + ); | |
56 | + | |
57 | + | |
58 | + if (!result) { | |
59 | + printk(KERN_ERR "rodata_test: test data was not read only\n"); | |
60 | + return -ENODEV; | |
61 | + } | |
62 | + | |
63 | + /* test 3: check the value hasn't changed */ | |
64 | + /* If this test fails, we managed to overwrite the data */ | |
65 | + if (!rodata_test_data) { | |
66 | + printk(KERN_ERR "rodata_test: Test 3 failes (end data)\n"); | |
67 | + return -ENODEV; | |
68 | + } | |
69 | + /* test 4: check if the rodata section is 4Kb aligned */ | |
70 | + start = (unsigned long)__start_rodata; | |
71 | + end = (unsigned long)__end_rodata; | |
72 | + if (start & (PAGE_SIZE - 1)) { | |
73 | + printk(KERN_ERR "rodata_test: .rodata is not 4k aligned\n"); | |
74 | + return -ENODEV; | |
75 | + } | |
76 | + if (end & (PAGE_SIZE - 1)) { | |
77 | + printk(KERN_ERR "rodata_test: .rodata end is not 4k aligned\n"); | |
78 | + return -ENODEV; | |
79 | + } | |
80 | + | |
81 | + return 0; | |
82 | +} | |
83 | + | |
84 | +MODULE_LICENSE("GPL"); | |
85 | +MODULE_DESCRIPTION("Testcase for the DEBUG_RODATA infrastructure"); | |
86 | +MODULE_AUTHOR("Arjan van de Ven <arjan@linux.intel.com>"); |
arch/x86/mm/init_32.c
... | ... | @@ -736,6 +736,8 @@ |
736 | 736 | } |
737 | 737 | |
738 | 738 | #ifdef CONFIG_DEBUG_RODATA |
739 | +const int rodata_test_data = 0xC3; | |
740 | +EXPORT_SYMBOL_GPL(rodata_test_data); | |
739 | 741 | |
740 | 742 | void mark_rodata_ro(void) |
741 | 743 | { |
... | ... | @@ -765,6 +767,7 @@ |
765 | 767 | set_pages_ro(virt_to_page(start), size >> PAGE_SHIFT); |
766 | 768 | printk("Write protecting the kernel read-only data: %luk\n", |
767 | 769 | size >> 10); |
770 | + rodata_test(); | |
768 | 771 | |
769 | 772 | #ifdef CONFIG_CPA_DEBUG |
770 | 773 | printk("Testing CPA: undo %lx-%lx\n", start, start + size); |
arch/x86/mm/init_64.c
... | ... | @@ -573,6 +573,7 @@ |
573 | 573 | set_memory_rw(begin, (end - begin)/PAGE_SIZE); |
574 | 574 | set_memory_np(begin, (end - begin)/PAGE_SIZE); |
575 | 575 | set_memory_nx(begin, (end - begin)/PAGE_SIZE); |
576 | + rodata_test(); | |
576 | 577 | } |
577 | 578 | #endif |
578 | 579 | } |
... | ... | @@ -585,6 +586,8 @@ |
585 | 586 | } |
586 | 587 | |
587 | 588 | #ifdef CONFIG_DEBUG_RODATA |
589 | +const int rodata_test_data = 0xC3; | |
590 | +EXPORT_SYMBOL_GPL(rodata_test_data); | |
588 | 591 | |
589 | 592 | void mark_rodata_ro(void) |
590 | 593 | { |
include/asm-generic/vmlinux.lds.h
include/asm-x86/cacheflush.h