x86: add testcases for RODATA and NX protections/attributes
[pandora-kernel.git] / 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>");