xref: /linux/drivers/misc/lkdtm/usercopy.c (revision 8e07e0e3964ca4e23ce7b68e2096fe660a888942)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * This is for all the tests related to copy_to_user() and copy_from_user()
4  * hardening.
5  */
6 #include "lkdtm.h"
7 #include <linux/slab.h>
8 #include <linux/highmem.h>
9 #include <linux/vmalloc.h>
10 #include <linux/sched/task_stack.h>
11 #include <linux/mman.h>
12 #include <linux/uaccess.h>
13 #include <asm/cacheflush.h>
14 
15 /*
16  * Many of the tests here end up using const sizes, but those would
17  * normally be ignored by hardened usercopy, so force the compiler
18  * into choosing the non-const path to make sure we trigger the
19  * hardened usercopy checks by added "unconst" to all the const copies,
20  * and making sure "cache_size" isn't optimized into a const.
21  */
22 static volatile size_t unconst;
23 static volatile size_t cache_size = 1024;
24 static struct kmem_cache *whitelist_cache;
25 
26 static const unsigned char test_text[] = "This is a test.\n";
27 
28 /*
29  * Instead of adding -Wno-return-local-addr, just pass the stack address
30  * through a function to obfuscate it from the compiler.
31  */
32 static noinline unsigned char *trick_compiler(unsigned char *stack)
33 {
34 	return stack + unconst;
35 }
36 
37 static noinline unsigned char *do_usercopy_stack_callee(int value)
38 {
39 	unsigned char buf[128];
40 	int i;
41 
42 	/* Exercise stack to avoid everything living in registers. */
43 	for (i = 0; i < sizeof(buf); i++) {
44 		buf[i] = value & 0xff;
45 	}
46 
47 	/*
48 	 * Put the target buffer in the middle of stack allocation
49 	 * so that we don't step on future stack users regardless
50 	 * of stack growth direction.
51 	 */
52 	return trick_compiler(&buf[(128/2)-32]);
53 }
54 
55 static noinline void do_usercopy_stack(bool to_user, bool bad_frame)
56 {
57 	unsigned long user_addr;
58 	unsigned char good_stack[32];
59 	unsigned char *bad_stack;
60 	int i;
61 
62 	/* Exercise stack to avoid everything living in registers. */
63 	for (i = 0; i < sizeof(good_stack); i++)
64 		good_stack[i] = test_text[i % sizeof(test_text)];
65 
66 	/* This is a pointer to outside our current stack frame. */
67 	if (bad_frame) {
68 		bad_stack = do_usercopy_stack_callee((uintptr_t)&bad_stack);
69 	} else {
70 		/* Put start address just inside stack. */
71 		bad_stack = task_stack_page(current) + THREAD_SIZE;
72 		bad_stack -= sizeof(unsigned long);
73 	}
74 
75 #ifdef ARCH_HAS_CURRENT_STACK_POINTER
76 	pr_info("stack     : %px\n", (void *)current_stack_pointer);
77 #endif
78 	pr_info("good_stack: %px-%px\n", good_stack, good_stack + sizeof(good_stack));
79 	pr_info("bad_stack : %px-%px\n", bad_stack, bad_stack + sizeof(good_stack));
80 
81 	user_addr = vm_mmap(NULL, 0, PAGE_SIZE,
82 			    PROT_READ | PROT_WRITE | PROT_EXEC,
83 			    MAP_ANONYMOUS | MAP_PRIVATE, 0);
84 	if (user_addr >= TASK_SIZE) {
85 		pr_warn("Failed to allocate user memory\n");
86 		return;
87 	}
88 
89 	if (to_user) {
90 		pr_info("attempting good copy_to_user of local stack\n");
91 		if (copy_to_user((void __user *)user_addr, good_stack,
92 				 unconst + sizeof(good_stack))) {
93 			pr_warn("copy_to_user failed unexpectedly?!\n");
94 			goto free_user;
95 		}
96 
97 		pr_info("attempting bad copy_to_user of distant stack\n");
98 		if (copy_to_user((void __user *)user_addr, bad_stack,
99 				 unconst + sizeof(good_stack))) {
100 			pr_warn("copy_to_user failed, but lacked Oops\n");
101 			goto free_user;
102 		}
103 	} else {
104 		/*
105 		 * There isn't a safe way to not be protected by usercopy
106 		 * if we're going to write to another thread's stack.
107 		 */
108 		if (!bad_frame)
109 			goto free_user;
110 
111 		pr_info("attempting good copy_from_user of local stack\n");
112 		if (copy_from_user(good_stack, (void __user *)user_addr,
113 				   unconst + sizeof(good_stack))) {
114 			pr_warn("copy_from_user failed unexpectedly?!\n");
115 			goto free_user;
116 		}
117 
118 		pr_info("attempting bad copy_from_user of distant stack\n");
119 		if (copy_from_user(bad_stack, (void __user *)user_addr,
120 				   unconst + sizeof(good_stack))) {
121 			pr_warn("copy_from_user failed, but lacked Oops\n");
122 			goto free_user;
123 		}
124 	}
125 
126 free_user:
127 	vm_munmap(user_addr, PAGE_SIZE);
128 }
129 
130 /*
131  * This checks for whole-object size validation with hardened usercopy,
132  * with or without usercopy whitelisting.
133  */
134 static void do_usercopy_slab_size(bool to_user)
135 {
136 	unsigned long user_addr;
137 	unsigned char *one, *two;
138 	void __user *test_user_addr;
139 	void *test_kern_addr;
140 	size_t size = unconst + 1024;
141 
142 	one = kmalloc(size, GFP_KERNEL);
143 	two = kmalloc(size, GFP_KERNEL);
144 	if (!one || !two) {
145 		pr_warn("Failed to allocate kernel memory\n");
146 		goto free_kernel;
147 	}
148 
149 	user_addr = vm_mmap(NULL, 0, PAGE_SIZE,
150 			    PROT_READ | PROT_WRITE | PROT_EXEC,
151 			    MAP_ANONYMOUS | MAP_PRIVATE, 0);
152 	if (user_addr >= TASK_SIZE) {
153 		pr_warn("Failed to allocate user memory\n");
154 		goto free_kernel;
155 	}
156 
157 	memset(one, 'A', size);
158 	memset(two, 'B', size);
159 
160 	test_user_addr = (void __user *)(user_addr + 16);
161 	test_kern_addr = one + 16;
162 
163 	if (to_user) {
164 		pr_info("attempting good copy_to_user of correct size\n");
165 		if (copy_to_user(test_user_addr, test_kern_addr, size / 2)) {
166 			pr_warn("copy_to_user failed unexpectedly?!\n");
167 			goto free_user;
168 		}
169 
170 		pr_info("attempting bad copy_to_user of too large size\n");
171 		if (copy_to_user(test_user_addr, test_kern_addr, size)) {
172 			pr_warn("copy_to_user failed, but lacked Oops\n");
173 			goto free_user;
174 		}
175 	} else {
176 		pr_info("attempting good copy_from_user of correct size\n");
177 		if (copy_from_user(test_kern_addr, test_user_addr, size / 2)) {
178 			pr_warn("copy_from_user failed unexpectedly?!\n");
179 			goto free_user;
180 		}
181 
182 		pr_info("attempting bad copy_from_user of too large size\n");
183 		if (copy_from_user(test_kern_addr, test_user_addr, size)) {
184 			pr_warn("copy_from_user failed, but lacked Oops\n");
185 			goto free_user;
186 		}
187 	}
188 	pr_err("FAIL: bad usercopy not detected!\n");
189 	pr_expected_config_param(CONFIG_HARDENED_USERCOPY, "hardened_usercopy");
190 
191 free_user:
192 	vm_munmap(user_addr, PAGE_SIZE);
193 free_kernel:
194 	kfree(one);
195 	kfree(two);
196 }
197 
198 /*
199  * This checks for the specific whitelist window within an object. If this
200  * test passes, then do_usercopy_slab_size() tests will pass too.
201  */
202 static void do_usercopy_slab_whitelist(bool to_user)
203 {
204 	unsigned long user_alloc;
205 	unsigned char *buf = NULL;
206 	unsigned char __user *user_addr;
207 	size_t offset, size;
208 
209 	/* Make sure cache was prepared. */
210 	if (!whitelist_cache) {
211 		pr_warn("Failed to allocate kernel cache\n");
212 		return;
213 	}
214 
215 	/*
216 	 * Allocate a buffer with a whitelisted window in the buffer.
217 	 */
218 	buf = kmem_cache_alloc(whitelist_cache, GFP_KERNEL);
219 	if (!buf) {
220 		pr_warn("Failed to allocate buffer from whitelist cache\n");
221 		goto free_alloc;
222 	}
223 
224 	/* Allocate user memory we'll poke at. */
225 	user_alloc = vm_mmap(NULL, 0, PAGE_SIZE,
226 			    PROT_READ | PROT_WRITE | PROT_EXEC,
227 			    MAP_ANONYMOUS | MAP_PRIVATE, 0);
228 	if (user_alloc >= TASK_SIZE) {
229 		pr_warn("Failed to allocate user memory\n");
230 		goto free_alloc;
231 	}
232 	user_addr = (void __user *)user_alloc;
233 
234 	memset(buf, 'B', cache_size);
235 
236 	/* Whitelisted window in buffer, from kmem_cache_create_usercopy. */
237 	offset = (cache_size / 4) + unconst;
238 	size = (cache_size / 16) + unconst;
239 
240 	if (to_user) {
241 		pr_info("attempting good copy_to_user inside whitelist\n");
242 		if (copy_to_user(user_addr, buf + offset, size)) {
243 			pr_warn("copy_to_user failed unexpectedly?!\n");
244 			goto free_user;
245 		}
246 
247 		pr_info("attempting bad copy_to_user outside whitelist\n");
248 		if (copy_to_user(user_addr, buf + offset - 1, size)) {
249 			pr_warn("copy_to_user failed, but lacked Oops\n");
250 			goto free_user;
251 		}
252 	} else {
253 		pr_info("attempting good copy_from_user inside whitelist\n");
254 		if (copy_from_user(buf + offset, user_addr, size)) {
255 			pr_warn("copy_from_user failed unexpectedly?!\n");
256 			goto free_user;
257 		}
258 
259 		pr_info("attempting bad copy_from_user outside whitelist\n");
260 		if (copy_from_user(buf + offset - 1, user_addr, size)) {
261 			pr_warn("copy_from_user failed, but lacked Oops\n");
262 			goto free_user;
263 		}
264 	}
265 	pr_err("FAIL: bad usercopy not detected!\n");
266 	pr_expected_config_param(CONFIG_HARDENED_USERCOPY, "hardened_usercopy");
267 
268 free_user:
269 	vm_munmap(user_alloc, PAGE_SIZE);
270 free_alloc:
271 	if (buf)
272 		kmem_cache_free(whitelist_cache, buf);
273 }
274 
275 /* Callable tests. */
276 static void lkdtm_USERCOPY_SLAB_SIZE_TO(void)
277 {
278 	do_usercopy_slab_size(true);
279 }
280 
281 static void lkdtm_USERCOPY_SLAB_SIZE_FROM(void)
282 {
283 	do_usercopy_slab_size(false);
284 }
285 
286 static void lkdtm_USERCOPY_SLAB_WHITELIST_TO(void)
287 {
288 	do_usercopy_slab_whitelist(true);
289 }
290 
291 static void lkdtm_USERCOPY_SLAB_WHITELIST_FROM(void)
292 {
293 	do_usercopy_slab_whitelist(false);
294 }
295 
296 static void lkdtm_USERCOPY_STACK_FRAME_TO(void)
297 {
298 	do_usercopy_stack(true, true);
299 }
300 
301 static void lkdtm_USERCOPY_STACK_FRAME_FROM(void)
302 {
303 	do_usercopy_stack(false, true);
304 }
305 
306 static void lkdtm_USERCOPY_STACK_BEYOND(void)
307 {
308 	do_usercopy_stack(true, false);
309 }
310 
311 static void lkdtm_USERCOPY_KERNEL(void)
312 {
313 	unsigned long user_addr;
314 
315 	user_addr = vm_mmap(NULL, 0, PAGE_SIZE,
316 			    PROT_READ | PROT_WRITE | PROT_EXEC,
317 			    MAP_ANONYMOUS | MAP_PRIVATE, 0);
318 	if (user_addr >= TASK_SIZE) {
319 		pr_warn("Failed to allocate user memory\n");
320 		return;
321 	}
322 
323 	pr_info("attempting good copy_to_user from kernel rodata: %px\n",
324 		test_text);
325 	if (copy_to_user((void __user *)user_addr, test_text,
326 			 unconst + sizeof(test_text))) {
327 		pr_warn("copy_to_user failed unexpectedly?!\n");
328 		goto free_user;
329 	}
330 
331 	pr_info("attempting bad copy_to_user from kernel text: %px\n",
332 		vm_mmap);
333 	if (copy_to_user((void __user *)user_addr, vm_mmap,
334 			 unconst + PAGE_SIZE)) {
335 		pr_warn("copy_to_user failed, but lacked Oops\n");
336 		goto free_user;
337 	}
338 	pr_err("FAIL: bad copy_to_user() not detected!\n");
339 	pr_expected_config_param(CONFIG_HARDENED_USERCOPY, "hardened_usercopy");
340 
341 free_user:
342 	vm_munmap(user_addr, PAGE_SIZE);
343 }
344 
345 /*
346  * This expects "kaddr" to point to a PAGE_SIZE allocation, which means
347  * a more complete test that would include copy_from_user() would risk
348  * memory corruption. Just test copy_to_user() here, as that exercises
349  * almost exactly the same code paths.
350  */
351 static void do_usercopy_page_span(const char *name, void *kaddr)
352 {
353 	unsigned long uaddr;
354 
355 	uaddr = vm_mmap(NULL, 0, PAGE_SIZE, PROT_READ | PROT_WRITE,
356 			MAP_ANONYMOUS | MAP_PRIVATE, 0);
357 	if (uaddr >= TASK_SIZE) {
358 		pr_warn("Failed to allocate user memory\n");
359 		return;
360 	}
361 
362 	/* Initialize contents. */
363 	memset(kaddr, 0xAA, PAGE_SIZE);
364 
365 	/* Bump the kaddr forward to detect a page-spanning overflow. */
366 	kaddr += PAGE_SIZE / 2;
367 
368 	pr_info("attempting good copy_to_user() from kernel %s: %px\n",
369 		name, kaddr);
370 	if (copy_to_user((void __user *)uaddr, kaddr,
371 			 unconst + (PAGE_SIZE / 2))) {
372 		pr_err("copy_to_user() failed unexpectedly?!\n");
373 		goto free_user;
374 	}
375 
376 	pr_info("attempting bad copy_to_user() from kernel %s: %px\n",
377 		name, kaddr);
378 	if (copy_to_user((void __user *)uaddr, kaddr, unconst + PAGE_SIZE)) {
379 		pr_warn("Good, copy_to_user() failed, but lacked Oops(?!)\n");
380 		goto free_user;
381 	}
382 
383 	pr_err("FAIL: bad copy_to_user() not detected!\n");
384 	pr_expected_config_param(CONFIG_HARDENED_USERCOPY, "hardened_usercopy");
385 
386 free_user:
387 	vm_munmap(uaddr, PAGE_SIZE);
388 }
389 
390 static void lkdtm_USERCOPY_VMALLOC(void)
391 {
392 	void *addr;
393 
394 	addr = vmalloc(PAGE_SIZE);
395 	if (!addr) {
396 		pr_err("vmalloc() failed!?\n");
397 		return;
398 	}
399 	do_usercopy_page_span("vmalloc", addr);
400 	vfree(addr);
401 }
402 
403 static void lkdtm_USERCOPY_FOLIO(void)
404 {
405 	struct folio *folio;
406 	void *addr;
407 
408 	/*
409 	 * FIXME: Folio checking currently misses 0-order allocations, so
410 	 * allocate and bump forward to the last page.
411 	 */
412 	folio = folio_alloc(GFP_KERNEL | __GFP_ZERO, 1);
413 	if (!folio) {
414 		pr_err("folio_alloc() failed!?\n");
415 		return;
416 	}
417 	addr = folio_address(folio);
418 	if (addr)
419 		do_usercopy_page_span("folio", addr + PAGE_SIZE);
420 	else
421 		pr_err("folio_address() failed?!\n");
422 	folio_put(folio);
423 }
424 
425 void __init lkdtm_usercopy_init(void)
426 {
427 	/* Prepare cache that lacks SLAB_USERCOPY flag. */
428 	whitelist_cache =
429 		kmem_cache_create_usercopy("lkdtm-usercopy", cache_size,
430 					   0, 0,
431 					   cache_size / 4,
432 					   cache_size / 16,
433 					   NULL);
434 }
435 
436 void __exit lkdtm_usercopy_exit(void)
437 {
438 	kmem_cache_destroy(whitelist_cache);
439 }
440 
441 static struct crashtype crashtypes[] = {
442 	CRASHTYPE(USERCOPY_SLAB_SIZE_TO),
443 	CRASHTYPE(USERCOPY_SLAB_SIZE_FROM),
444 	CRASHTYPE(USERCOPY_SLAB_WHITELIST_TO),
445 	CRASHTYPE(USERCOPY_SLAB_WHITELIST_FROM),
446 	CRASHTYPE(USERCOPY_STACK_FRAME_TO),
447 	CRASHTYPE(USERCOPY_STACK_FRAME_FROM),
448 	CRASHTYPE(USERCOPY_STACK_BEYOND),
449 	CRASHTYPE(USERCOPY_VMALLOC),
450 	CRASHTYPE(USERCOPY_FOLIO),
451 	CRASHTYPE(USERCOPY_KERNEL),
452 };
453 
454 struct crashtype_category usercopy_crashtypes = {
455 	.crashtypes = crashtypes,
456 	.len	    = ARRAY_SIZE(crashtypes),
457 };
458