• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *
3  * Copyright (c) 2014 Samsung Electronics Co., Ltd.
4  * Author: Andrey Ryabinin <a.ryabinin@samsung.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  *
10  */
11 
12 #define pr_fmt(fmt) "kasan test: %s " fmt, __func__
13 
14 #include <linux/kernel.h>
15 #include <linux/mman.h>
16 #include <linux/mm.h>
17 #include <linux/printk.h>
18 #include <linux/slab.h>
19 #include <linux/string.h>
20 #include <linux/uaccess.h>
21 #include <linux/module.h>
22 #include <linux/kasan.h>
23 
24 /*
25  * Note: test functions are marked noinline so that their names appear in
26  * reports.
27  */
28 
kmalloc_oob_right(void)29 static noinline void __init kmalloc_oob_right(void)
30 {
31 	char *ptr;
32 	size_t size = 123;
33 
34 	pr_info("out-of-bounds to right\n");
35 	ptr = kmalloc(size, GFP_KERNEL);
36 	if (!ptr) {
37 		pr_err("Allocation failed\n");
38 		return;
39 	}
40 
41 	ptr[size] = 'x';
42 	kfree(ptr);
43 }
44 
kmalloc_oob_left(void)45 static noinline void __init kmalloc_oob_left(void)
46 {
47 	char *ptr;
48 	size_t size = 15;
49 
50 	pr_info("out-of-bounds to left\n");
51 	ptr = kmalloc(size, GFP_KERNEL);
52 	if (!ptr) {
53 		pr_err("Allocation failed\n");
54 		return;
55 	}
56 
57 	*ptr = *(ptr - 1);
58 	kfree(ptr);
59 }
60 
kmalloc_node_oob_right(void)61 static noinline void __init kmalloc_node_oob_right(void)
62 {
63 	char *ptr;
64 	size_t size = 4096;
65 
66 	pr_info("kmalloc_node(): out-of-bounds to right\n");
67 	ptr = kmalloc_node(size, GFP_KERNEL, 0);
68 	if (!ptr) {
69 		pr_err("Allocation failed\n");
70 		return;
71 	}
72 
73 	ptr[size] = 0;
74 	kfree(ptr);
75 }
76 
77 #ifdef CONFIG_SLUB
kmalloc_pagealloc_oob_right(void)78 static noinline void __init kmalloc_pagealloc_oob_right(void)
79 {
80 	char *ptr;
81 	size_t size = KMALLOC_MAX_CACHE_SIZE + 10;
82 
83 	/* Allocate a chunk that does not fit into a SLUB cache to trigger
84 	 * the page allocator fallback.
85 	 */
86 	pr_info("kmalloc pagealloc allocation: out-of-bounds to right\n");
87 	ptr = kmalloc(size, GFP_KERNEL);
88 	if (!ptr) {
89 		pr_err("Allocation failed\n");
90 		return;
91 	}
92 
93 	ptr[size] = 0;
94 	kfree(ptr);
95 }
96 #endif
97 
kmalloc_large_oob_right(void)98 static noinline void __init kmalloc_large_oob_right(void)
99 {
100 	char *ptr;
101 	size_t size = KMALLOC_MAX_CACHE_SIZE - 256;
102 	/* Allocate a chunk that is large enough, but still fits into a slab
103 	 * and does not trigger the page allocator fallback in SLUB.
104 	 */
105 	pr_info("kmalloc large allocation: out-of-bounds to right\n");
106 	ptr = kmalloc(size, GFP_KERNEL);
107 	if (!ptr) {
108 		pr_err("Allocation failed\n");
109 		return;
110 	}
111 
112 	ptr[size] = 0;
113 	kfree(ptr);
114 }
115 
kmalloc_oob_krealloc_more(void)116 static noinline void __init kmalloc_oob_krealloc_more(void)
117 {
118 	char *ptr1, *ptr2;
119 	size_t size1 = 17;
120 	size_t size2 = 19;
121 
122 	pr_info("out-of-bounds after krealloc more\n");
123 	ptr1 = kmalloc(size1, GFP_KERNEL);
124 	ptr2 = krealloc(ptr1, size2, GFP_KERNEL);
125 	if (!ptr1 || !ptr2) {
126 		pr_err("Allocation failed\n");
127 		kfree(ptr1);
128 		return;
129 	}
130 
131 	ptr2[size2] = 'x';
132 	kfree(ptr2);
133 }
134 
kmalloc_oob_krealloc_less(void)135 static noinline void __init kmalloc_oob_krealloc_less(void)
136 {
137 	char *ptr1, *ptr2;
138 	size_t size1 = 17;
139 	size_t size2 = 15;
140 
141 	pr_info("out-of-bounds after krealloc less\n");
142 	ptr1 = kmalloc(size1, GFP_KERNEL);
143 	ptr2 = krealloc(ptr1, size2, GFP_KERNEL);
144 	if (!ptr1 || !ptr2) {
145 		pr_err("Allocation failed\n");
146 		kfree(ptr1);
147 		return;
148 	}
149 	ptr2[size2] = 'x';
150 	kfree(ptr2);
151 }
152 
kmalloc_oob_16(void)153 static noinline void __init kmalloc_oob_16(void)
154 {
155 	struct {
156 		u64 words[2];
157 	} *ptr1, *ptr2;
158 
159 	pr_info("kmalloc out-of-bounds for 16-bytes access\n");
160 	ptr1 = kmalloc(sizeof(*ptr1) - 3, GFP_KERNEL);
161 	ptr2 = kmalloc(sizeof(*ptr2), GFP_KERNEL);
162 	if (!ptr1 || !ptr2) {
163 		pr_err("Allocation failed\n");
164 		kfree(ptr1);
165 		kfree(ptr2);
166 		return;
167 	}
168 	*ptr1 = *ptr2;
169 	kfree(ptr1);
170 	kfree(ptr2);
171 }
172 
kmalloc_oob_memset_2(void)173 static noinline void __init kmalloc_oob_memset_2(void)
174 {
175 	char *ptr;
176 	size_t size = 8;
177 
178 	pr_info("out-of-bounds in memset2\n");
179 	ptr = kmalloc(size, GFP_KERNEL);
180 	if (!ptr) {
181 		pr_err("Allocation failed\n");
182 		return;
183 	}
184 
185 	memset(ptr+7, 0, 2);
186 	kfree(ptr);
187 }
188 
kmalloc_oob_memset_4(void)189 static noinline void __init kmalloc_oob_memset_4(void)
190 {
191 	char *ptr;
192 	size_t size = 8;
193 
194 	pr_info("out-of-bounds in memset4\n");
195 	ptr = kmalloc(size, GFP_KERNEL);
196 	if (!ptr) {
197 		pr_err("Allocation failed\n");
198 		return;
199 	}
200 
201 	memset(ptr+5, 0, 4);
202 	kfree(ptr);
203 }
204 
205 
kmalloc_oob_memset_8(void)206 static noinline void __init kmalloc_oob_memset_8(void)
207 {
208 	char *ptr;
209 	size_t size = 8;
210 
211 	pr_info("out-of-bounds in memset8\n");
212 	ptr = kmalloc(size, GFP_KERNEL);
213 	if (!ptr) {
214 		pr_err("Allocation failed\n");
215 		return;
216 	}
217 
218 	memset(ptr+1, 0, 8);
219 	kfree(ptr);
220 }
221 
kmalloc_oob_memset_16(void)222 static noinline void __init kmalloc_oob_memset_16(void)
223 {
224 	char *ptr;
225 	size_t size = 16;
226 
227 	pr_info("out-of-bounds in memset16\n");
228 	ptr = kmalloc(size, GFP_KERNEL);
229 	if (!ptr) {
230 		pr_err("Allocation failed\n");
231 		return;
232 	}
233 
234 	memset(ptr+1, 0, 16);
235 	kfree(ptr);
236 }
237 
kmalloc_oob_in_memset(void)238 static noinline void __init kmalloc_oob_in_memset(void)
239 {
240 	char *ptr;
241 	size_t size = 666;
242 
243 	pr_info("out-of-bounds in memset\n");
244 	ptr = kmalloc(size, GFP_KERNEL);
245 	if (!ptr) {
246 		pr_err("Allocation failed\n");
247 		return;
248 	}
249 
250 	memset(ptr, 0, size+5);
251 	kfree(ptr);
252 }
253 
kmalloc_uaf(void)254 static noinline void __init kmalloc_uaf(void)
255 {
256 	char *ptr;
257 	size_t size = 10;
258 
259 	pr_info("use-after-free\n");
260 	ptr = kmalloc(size, GFP_KERNEL);
261 	if (!ptr) {
262 		pr_err("Allocation failed\n");
263 		return;
264 	}
265 
266 	kfree(ptr);
267 	*(ptr + 8) = 'x';
268 }
269 
kmalloc_uaf_memset(void)270 static noinline void __init kmalloc_uaf_memset(void)
271 {
272 	char *ptr;
273 	size_t size = 33;
274 
275 	pr_info("use-after-free in memset\n");
276 	ptr = kmalloc(size, GFP_KERNEL);
277 	if (!ptr) {
278 		pr_err("Allocation failed\n");
279 		return;
280 	}
281 
282 	kfree(ptr);
283 	memset(ptr, 0, size);
284 }
285 
kmalloc_uaf2(void)286 static noinline void __init kmalloc_uaf2(void)
287 {
288 	char *ptr1, *ptr2;
289 	size_t size = 43;
290 
291 	pr_info("use-after-free after another kmalloc\n");
292 	ptr1 = kmalloc(size, GFP_KERNEL);
293 	if (!ptr1) {
294 		pr_err("Allocation failed\n");
295 		return;
296 	}
297 
298 	kfree(ptr1);
299 	ptr2 = kmalloc(size, GFP_KERNEL);
300 	if (!ptr2) {
301 		pr_err("Allocation failed\n");
302 		return;
303 	}
304 
305 	ptr1[40] = 'x';
306 	if (ptr1 == ptr2)
307 		pr_err("Could not detect use-after-free: ptr1 == ptr2\n");
308 	kfree(ptr2);
309 }
310 
kmem_cache_oob(void)311 static noinline void __init kmem_cache_oob(void)
312 {
313 	char *p;
314 	size_t size = 200;
315 	struct kmem_cache *cache = kmem_cache_create("test_cache",
316 						size, 0,
317 						0, NULL);
318 	if (!cache) {
319 		pr_err("Cache allocation failed\n");
320 		return;
321 	}
322 	pr_info("out-of-bounds in kmem_cache_alloc\n");
323 	p = kmem_cache_alloc(cache, GFP_KERNEL);
324 	if (!p) {
325 		pr_err("Allocation failed\n");
326 		kmem_cache_destroy(cache);
327 		return;
328 	}
329 
330 	*p = p[size];
331 	kmem_cache_free(cache, p);
332 	kmem_cache_destroy(cache);
333 }
334 
335 static char global_array[10];
336 
kasan_global_oob(void)337 static noinline void __init kasan_global_oob(void)
338 {
339 	volatile int i = 3;
340 	char *p = &global_array[ARRAY_SIZE(global_array) + i];
341 
342 	pr_info("out-of-bounds global variable\n");
343 	*(volatile char *)p;
344 }
345 
kasan_stack_oob(void)346 static noinline void __init kasan_stack_oob(void)
347 {
348 	char stack_array[10];
349 	volatile int i = 0;
350 	char *p = &stack_array[ARRAY_SIZE(stack_array) + i];
351 
352 	pr_info("out-of-bounds on stack\n");
353 	*(volatile char *)p;
354 }
355 
ksize_unpoisons_memory(void)356 static noinline void __init ksize_unpoisons_memory(void)
357 {
358 	char *ptr;
359 	size_t size = 123, real_size = size;
360 
361 	pr_info("ksize() unpoisons the whole allocated chunk\n");
362 	ptr = kmalloc(size, GFP_KERNEL);
363 	if (!ptr) {
364 		pr_err("Allocation failed\n");
365 		return;
366 	}
367 	real_size = ksize(ptr);
368 	/* This access doesn't trigger an error. */
369 	ptr[size] = 'x';
370 	/* This one does. */
371 	ptr[real_size] = 'y';
372 	kfree(ptr);
373 }
374 
copy_user_test(void)375 static noinline void __init copy_user_test(void)
376 {
377 	char *kmem;
378 	char __user *usermem;
379 	size_t size = 10;
380 	int unused;
381 
382 	kmem = kmalloc(size, GFP_KERNEL);
383 	if (!kmem)
384 		return;
385 
386 	usermem = (char __user *)vm_mmap(NULL, 0, PAGE_SIZE,
387 			    PROT_READ | PROT_WRITE | PROT_EXEC,
388 			    MAP_ANONYMOUS | MAP_PRIVATE, 0);
389 	if (IS_ERR(usermem)) {
390 		pr_err("Failed to allocate user memory\n");
391 		kfree(kmem);
392 		return;
393 	}
394 
395 	pr_info("out-of-bounds in copy_from_user()\n");
396 	unused = copy_from_user(kmem, usermem, size + 1);
397 
398 	pr_info("out-of-bounds in copy_to_user()\n");
399 	unused = copy_to_user(usermem, kmem, size + 1);
400 
401 	pr_info("out-of-bounds in __copy_from_user()\n");
402 	unused = __copy_from_user(kmem, usermem, size + 1);
403 
404 	pr_info("out-of-bounds in __copy_to_user()\n");
405 	unused = __copy_to_user(usermem, kmem, size + 1);
406 
407 	pr_info("out-of-bounds in __copy_from_user_inatomic()\n");
408 	unused = __copy_from_user_inatomic(kmem, usermem, size + 1);
409 
410 	pr_info("out-of-bounds in __copy_to_user_inatomic()\n");
411 	unused = __copy_to_user_inatomic(usermem, kmem, size + 1);
412 
413 	pr_info("out-of-bounds in strncpy_from_user()\n");
414 	unused = strncpy_from_user(kmem, usermem, size + 1);
415 
416 	vm_munmap((unsigned long)usermem, PAGE_SIZE);
417 	kfree(kmem);
418 }
419 
use_after_scope_test(void)420 static noinline void __init use_after_scope_test(void)
421 {
422 	volatile char *volatile p;
423 
424 	pr_info("use-after-scope on int\n");
425 	{
426 		int local = 0;
427 
428 		p = (char *)&local;
429 	}
430 	p[0] = 1;
431 	p[3] = 1;
432 
433 	pr_info("use-after-scope on array\n");
434 	{
435 		char local[1024] = {0};
436 
437 		p = local;
438 	}
439 	p[0] = 1;
440 	p[1023] = 1;
441 }
442 
kasan_alloca_oob_left(void)443 static noinline void __init kasan_alloca_oob_left(void)
444 {
445 	volatile int i = 10;
446 	char alloca_array[i];
447 	char *p = alloca_array - 1;
448 
449 	pr_info("out-of-bounds to left on alloca\n");
450 	*(volatile char *)p;
451 }
452 
kasan_alloca_oob_right(void)453 static noinline void __init kasan_alloca_oob_right(void)
454 {
455 	volatile int i = 10;
456 	char alloca_array[i];
457 	char *p = alloca_array + i;
458 
459 	pr_info("out-of-bounds to right on alloca\n");
460 	*(volatile char *)p;
461 }
462 
kmalloc_tests_init(void)463 static int __init kmalloc_tests_init(void)
464 {
465 	/*
466 	 * Temporarily enable multi-shot mode. Otherwise, we'd only get a
467 	 * report for the first case.
468 	 */
469 	bool multishot = kasan_save_enable_multi_shot();
470 
471 	kmalloc_oob_right();
472 	kmalloc_oob_left();
473 	kmalloc_node_oob_right();
474 #ifdef CONFIG_SLUB
475 	kmalloc_pagealloc_oob_right();
476 #endif
477 	kmalloc_large_oob_right();
478 	kmalloc_oob_krealloc_more();
479 	kmalloc_oob_krealloc_less();
480 	kmalloc_oob_16();
481 	kmalloc_oob_in_memset();
482 	kmalloc_oob_memset_2();
483 	kmalloc_oob_memset_4();
484 	kmalloc_oob_memset_8();
485 	kmalloc_oob_memset_16();
486 	kmalloc_uaf();
487 	kmalloc_uaf_memset();
488 	kmalloc_uaf2();
489 	kmem_cache_oob();
490 	kasan_stack_oob();
491 	kasan_global_oob();
492 	kasan_alloca_oob_left();
493 	kasan_alloca_oob_right();
494 	ksize_unpoisons_memory();
495 	copy_user_test();
496 	use_after_scope_test();
497 
498 	kasan_restore_multi_shot(multishot);
499 
500 	return -EAGAIN;
501 }
502 
503 module_init(kmalloc_tests_init);
504 MODULE_LICENSE("GPL");
505