1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * This is for all the tests related to validating kernel memory |
4 | * permissions: non-executable regions, non-writable regions, and |
5 | * even non-readable regions. |
6 | */ |
7 | #include "lkdtm.h" |
8 | #include <linux/slab.h> |
9 | #include <linux/vmalloc.h> |
10 | #include <linux/mman.h> |
11 | #include <linux/uaccess.h> |
12 | #include <asm/cacheflush.h> |
13 | #include <asm/sections.h> |
14 | |
15 | /* Whether or not to fill the target memory area with do_nothing(). */ |
16 | #define CODE_WRITE true |
17 | #define CODE_AS_IS false |
18 | |
19 | /* How many bytes to copy to be sure we've copied enough of do_nothing(). */ |
20 | #define EXEC_SIZE 64 |
21 | |
22 | /* This is non-const, so it will end up in the .data section. */ |
23 | static u8 data_area[EXEC_SIZE]; |
24 | |
25 | /* This is const, so it will end up in the .rodata section. */ |
26 | static const unsigned long rodata = 0xAA55AA55; |
27 | |
28 | /* This is marked __ro_after_init, so it should ultimately be .rodata. */ |
29 | static unsigned long ro_after_init __ro_after_init = 0x55AA5500; |
30 | |
31 | /* |
32 | * This just returns to the caller. It is designed to be copied into |
33 | * non-executable memory regions. |
34 | */ |
35 | static noinline void do_nothing(void) |
36 | { |
37 | return; |
38 | } |
39 | |
40 | /* Must immediately follow do_nothing for size calculuations to work out. */ |
41 | static noinline void do_overwritten(void) |
42 | { |
43 | pr_info("do_overwritten wasn't overwritten!\n" ); |
44 | return; |
45 | } |
46 | |
47 | static noinline void do_almost_nothing(void) |
48 | { |
49 | pr_info("do_nothing was hijacked!\n" ); |
50 | } |
51 | |
52 | static void *setup_function_descriptor(func_desc_t *fdesc, void *dst) |
53 | { |
54 | if (!have_function_descriptors()) |
55 | return dst; |
56 | |
57 | memcpy(fdesc, do_nothing, sizeof(*fdesc)); |
58 | fdesc->addr = (unsigned long)dst; |
59 | barrier(); |
60 | |
61 | return fdesc; |
62 | } |
63 | |
64 | static noinline void execute_location(void *dst, bool write) |
65 | { |
66 | void (*func)(void); |
67 | func_desc_t fdesc; |
68 | void *do_nothing_text = dereference_function_descriptor(do_nothing); |
69 | |
70 | pr_info("attempting ok execution at %px\n" , do_nothing_text); |
71 | do_nothing(); |
72 | |
73 | if (write == CODE_WRITE) { |
74 | memcpy(dst, do_nothing_text, EXEC_SIZE); |
75 | flush_icache_range(start: (unsigned long)dst, |
76 | end: (unsigned long)dst + EXEC_SIZE); |
77 | } |
78 | pr_info("attempting bad execution at %px\n" , dst); |
79 | func = setup_function_descriptor(fdesc: &fdesc, dst); |
80 | func(); |
81 | pr_err("FAIL: func returned\n" ); |
82 | } |
83 | |
84 | static void execute_user_location(void *dst) |
85 | { |
86 | int copied; |
87 | |
88 | /* Intentionally crossing kernel/user memory boundary. */ |
89 | void (*func)(void); |
90 | func_desc_t fdesc; |
91 | void *do_nothing_text = dereference_function_descriptor(do_nothing); |
92 | |
93 | pr_info("attempting ok execution at %px\n" , do_nothing_text); |
94 | do_nothing(); |
95 | |
96 | copied = access_process_vm(current, addr: (unsigned long)dst, buf: do_nothing_text, |
97 | EXEC_SIZE, gup_flags: FOLL_WRITE); |
98 | if (copied < EXEC_SIZE) |
99 | return; |
100 | pr_info("attempting bad execution at %px\n" , dst); |
101 | func = setup_function_descriptor(fdesc: &fdesc, dst); |
102 | func(); |
103 | pr_err("FAIL: func returned\n" ); |
104 | } |
105 | |
106 | static void lkdtm_WRITE_RO(void) |
107 | { |
108 | /* Explicitly cast away "const" for the test and make volatile. */ |
109 | volatile unsigned long *ptr = (unsigned long *)&rodata; |
110 | |
111 | pr_info("attempting bad rodata write at %px\n" , ptr); |
112 | *ptr ^= 0xabcd1234; |
113 | pr_err("FAIL: survived bad write\n" ); |
114 | } |
115 | |
116 | static void lkdtm_WRITE_RO_AFTER_INIT(void) |
117 | { |
118 | volatile unsigned long *ptr = &ro_after_init; |
119 | |
120 | /* |
121 | * Verify we were written to during init. Since an Oops |
122 | * is considered a "success", a failure is to just skip the |
123 | * real test. |
124 | */ |
125 | if ((*ptr & 0xAA) != 0xAA) { |
126 | pr_info("%p was NOT written during init!?\n" , ptr); |
127 | return; |
128 | } |
129 | |
130 | pr_info("attempting bad ro_after_init write at %px\n" , ptr); |
131 | *ptr ^= 0xabcd1234; |
132 | pr_err("FAIL: survived bad write\n" ); |
133 | } |
134 | |
135 | static void lkdtm_WRITE_KERN(void) |
136 | { |
137 | size_t size; |
138 | volatile unsigned char *ptr; |
139 | |
140 | size = (unsigned long)dereference_function_descriptor(do_overwritten) - |
141 | (unsigned long)dereference_function_descriptor(do_nothing); |
142 | ptr = dereference_function_descriptor(do_overwritten); |
143 | |
144 | pr_info("attempting bad %zu byte write at %px\n" , size, ptr); |
145 | memcpy((void *)ptr, (unsigned char *)do_nothing, size); |
146 | flush_icache_range(start: (unsigned long)ptr, end: (unsigned long)(ptr + size)); |
147 | pr_err("FAIL: survived bad write\n" ); |
148 | |
149 | do_overwritten(); |
150 | } |
151 | |
152 | static void lkdtm_WRITE_OPD(void) |
153 | { |
154 | size_t size = sizeof(func_desc_t); |
155 | void (*func)(void) = do_nothing; |
156 | |
157 | if (!have_function_descriptors()) { |
158 | pr_info("XFAIL: Platform doesn't use function descriptors.\n" ); |
159 | return; |
160 | } |
161 | pr_info("attempting bad %zu bytes write at %px\n" , size, do_nothing); |
162 | memcpy(do_nothing, do_almost_nothing, size); |
163 | pr_err("FAIL: survived bad write\n" ); |
164 | |
165 | asm("" : "=m" (func)); |
166 | func(); |
167 | } |
168 | |
169 | static void lkdtm_EXEC_DATA(void) |
170 | { |
171 | execute_location(dst: data_area, CODE_WRITE); |
172 | } |
173 | |
174 | static void lkdtm_EXEC_STACK(void) |
175 | { |
176 | u8 stack_area[EXEC_SIZE]; |
177 | execute_location(dst: stack_area, CODE_WRITE); |
178 | } |
179 | |
180 | static void lkdtm_EXEC_KMALLOC(void) |
181 | { |
182 | u32 *kmalloc_area = kmalloc(EXEC_SIZE, GFP_KERNEL); |
183 | execute_location(dst: kmalloc_area, CODE_WRITE); |
184 | kfree(objp: kmalloc_area); |
185 | } |
186 | |
187 | static void lkdtm_EXEC_VMALLOC(void) |
188 | { |
189 | u32 *vmalloc_area = vmalloc(EXEC_SIZE); |
190 | execute_location(dst: vmalloc_area, CODE_WRITE); |
191 | vfree(addr: vmalloc_area); |
192 | } |
193 | |
194 | static void lkdtm_EXEC_RODATA(void) |
195 | { |
196 | execute_location(dereference_function_descriptor(lkdtm_rodata_do_nothing), |
197 | CODE_AS_IS); |
198 | } |
199 | |
200 | static void lkdtm_EXEC_USERSPACE(void) |
201 | { |
202 | unsigned long user_addr; |
203 | |
204 | user_addr = vm_mmap(NULL, 0, PAGE_SIZE, |
205 | PROT_READ | PROT_WRITE | PROT_EXEC, |
206 | MAP_ANONYMOUS | MAP_PRIVATE, 0); |
207 | if (user_addr >= TASK_SIZE) { |
208 | pr_warn("Failed to allocate user memory\n" ); |
209 | return; |
210 | } |
211 | execute_user_location(dst: (void *)user_addr); |
212 | vm_munmap(user_addr, PAGE_SIZE); |
213 | } |
214 | |
215 | static void lkdtm_EXEC_NULL(void) |
216 | { |
217 | execute_location(NULL, CODE_AS_IS); |
218 | } |
219 | |
220 | static void lkdtm_ACCESS_USERSPACE(void) |
221 | { |
222 | unsigned long user_addr, tmp = 0; |
223 | unsigned long *ptr; |
224 | |
225 | user_addr = vm_mmap(NULL, 0, PAGE_SIZE, |
226 | PROT_READ | PROT_WRITE | PROT_EXEC, |
227 | MAP_ANONYMOUS | MAP_PRIVATE, 0); |
228 | if (user_addr >= TASK_SIZE) { |
229 | pr_warn("Failed to allocate user memory\n" ); |
230 | return; |
231 | } |
232 | |
233 | if (copy_to_user(to: (void __user *)user_addr, from: &tmp, n: sizeof(tmp))) { |
234 | pr_warn("copy_to_user failed\n" ); |
235 | vm_munmap(user_addr, PAGE_SIZE); |
236 | return; |
237 | } |
238 | |
239 | ptr = (unsigned long *)user_addr; |
240 | |
241 | pr_info("attempting bad read at %px\n" , ptr); |
242 | tmp = *ptr; |
243 | tmp += 0xc0dec0de; |
244 | pr_err("FAIL: survived bad read\n" ); |
245 | |
246 | pr_info("attempting bad write at %px\n" , ptr); |
247 | *ptr = tmp; |
248 | pr_err("FAIL: survived bad write\n" ); |
249 | |
250 | vm_munmap(user_addr, PAGE_SIZE); |
251 | } |
252 | |
253 | static void lkdtm_ACCESS_NULL(void) |
254 | { |
255 | unsigned long tmp; |
256 | volatile unsigned long *ptr = (unsigned long *)NULL; |
257 | |
258 | pr_info("attempting bad read at %px\n" , ptr); |
259 | tmp = *ptr; |
260 | tmp += 0xc0dec0de; |
261 | pr_err("FAIL: survived bad read\n" ); |
262 | |
263 | pr_info("attempting bad write at %px\n" , ptr); |
264 | *ptr = tmp; |
265 | pr_err("FAIL: survived bad write\n" ); |
266 | } |
267 | |
268 | void __init lkdtm_perms_init(void) |
269 | { |
270 | /* Make sure we can write to __ro_after_init values during __init */ |
271 | ro_after_init |= 0xAA; |
272 | } |
273 | |
274 | static struct crashtype crashtypes[] = { |
275 | CRASHTYPE(WRITE_RO), |
276 | CRASHTYPE(WRITE_RO_AFTER_INIT), |
277 | CRASHTYPE(WRITE_KERN), |
278 | CRASHTYPE(WRITE_OPD), |
279 | CRASHTYPE(EXEC_DATA), |
280 | CRASHTYPE(EXEC_STACK), |
281 | CRASHTYPE(EXEC_KMALLOC), |
282 | CRASHTYPE(EXEC_VMALLOC), |
283 | CRASHTYPE(EXEC_RODATA), |
284 | CRASHTYPE(EXEC_USERSPACE), |
285 | CRASHTYPE(EXEC_NULL), |
286 | CRASHTYPE(ACCESS_USERSPACE), |
287 | CRASHTYPE(ACCESS_NULL), |
288 | }; |
289 | |
290 | struct crashtype_category perms_crashtypes = { |
291 | .crashtypes = crashtypes, |
292 | .len = ARRAY_SIZE(crashtypes), |
293 | }; |
294 | |