GNU Linux-libre 4.9.314-gnu1
[releases.git] / lib / test_kasan.c
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
23 /*
24  * Note: test functions are marked noinline so that their names appear in
25  * reports.
26  */
27
28 static noinline void __init kmalloc_oob_right(void)
29 {
30         char *ptr;
31         size_t size = 123;
32
33         pr_info("out-of-bounds to right\n");
34         ptr = kmalloc(size, GFP_KERNEL);
35         if (!ptr) {
36                 pr_err("Allocation failed\n");
37                 return;
38         }
39
40         ptr[size] = 'x';
41         kfree(ptr);
42 }
43
44 static noinline void __init kmalloc_oob_left(void)
45 {
46         char *ptr;
47         size_t size = 15;
48
49         pr_info("out-of-bounds to left\n");
50         ptr = kmalloc(size, GFP_KERNEL);
51         if (!ptr) {
52                 pr_err("Allocation failed\n");
53                 return;
54         }
55
56         *ptr = *(ptr - 1);
57         kfree(ptr);
58 }
59
60 static noinline void __init kmalloc_node_oob_right(void)
61 {
62         char *ptr;
63         size_t size = 4096;
64
65         pr_info("kmalloc_node(): out-of-bounds to right\n");
66         ptr = kmalloc_node(size, GFP_KERNEL, 0);
67         if (!ptr) {
68                 pr_err("Allocation failed\n");
69                 return;
70         }
71
72         ptr[size] = 0;
73         kfree(ptr);
74 }
75
76 #ifdef CONFIG_SLUB
77 static noinline void __init kmalloc_pagealloc_oob_right(void)
78 {
79         char *ptr;
80         size_t size = KMALLOC_MAX_CACHE_SIZE + 10;
81
82         /* Allocate a chunk that does not fit into a SLUB cache to trigger
83          * the page allocator fallback.
84          */
85         pr_info("kmalloc pagealloc allocation: out-of-bounds to right\n");
86         ptr = kmalloc(size, GFP_KERNEL);
87         if (!ptr) {
88                 pr_err("Allocation failed\n");
89                 return;
90         }
91
92         ptr[size] = 0;
93         kfree(ptr);
94 }
95 #endif
96
97 static noinline void __init kmalloc_large_oob_right(void)
98 {
99         char *ptr;
100         size_t size = KMALLOC_MAX_CACHE_SIZE - 256;
101         /* Allocate a chunk that is large enough, but still fits into a slab
102          * and does not trigger the page allocator fallback in SLUB.
103          */
104         pr_info("kmalloc large allocation: out-of-bounds to right\n");
105         ptr = kmalloc(size, GFP_KERNEL);
106         if (!ptr) {
107                 pr_err("Allocation failed\n");
108                 return;
109         }
110
111         ptr[size] = 0;
112         kfree(ptr);
113 }
114
115 static noinline void __init kmalloc_oob_krealloc_more(void)
116 {
117         char *ptr1, *ptr2;
118         size_t size1 = 17;
119         size_t size2 = 19;
120
121         pr_info("out-of-bounds after krealloc more\n");
122         ptr1 = kmalloc(size1, GFP_KERNEL);
123         ptr2 = krealloc(ptr1, size2, GFP_KERNEL);
124         if (!ptr1 || !ptr2) {
125                 pr_err("Allocation failed\n");
126                 kfree(ptr1);
127                 kfree(ptr2);
128                 return;
129         }
130
131         ptr2[size2] = 'x';
132         kfree(ptr2);
133 }
134
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
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
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
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
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
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
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
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
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
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
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
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
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
356 static noinline void __init ksize_unpoisons_memory(void)
357 {
358         char *ptr;
359         size_t size = 123, real_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
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
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
443 static int __init kmalloc_tests_init(void)
444 {
445         kmalloc_oob_right();
446         kmalloc_oob_left();
447         kmalloc_node_oob_right();
448 #ifdef CONFIG_SLUB
449         kmalloc_pagealloc_oob_right();
450 #endif
451         kmalloc_large_oob_right();
452         kmalloc_oob_krealloc_more();
453         kmalloc_oob_krealloc_less();
454         kmalloc_oob_16();
455         kmalloc_oob_in_memset();
456         kmalloc_oob_memset_2();
457         kmalloc_oob_memset_4();
458         kmalloc_oob_memset_8();
459         kmalloc_oob_memset_16();
460         kmalloc_uaf();
461         kmalloc_uaf_memset();
462         kmalloc_uaf2();
463         kmem_cache_oob();
464         kasan_stack_oob();
465         kasan_global_oob();
466         ksize_unpoisons_memory();
467         copy_user_test();
468         use_after_scope_test();
469         return -EAGAIN;
470 }
471
472 module_init(kmalloc_tests_init);
473 MODULE_LICENSE("GPL");