Merge remote-tracking branch 'ovl/misc' into work.misc
[cascardo/linux.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 static noinline void __init kmalloc_oob_right(void)
24 {
25         char *ptr;
26         size_t size = 123;
27
28         pr_info("out-of-bounds to right\n");
29         ptr = kmalloc(size, GFP_KERNEL);
30         if (!ptr) {
31                 pr_err("Allocation failed\n");
32                 return;
33         }
34
35         ptr[size] = 'x';
36         kfree(ptr);
37 }
38
39 static noinline void __init kmalloc_oob_left(void)
40 {
41         char *ptr;
42         size_t size = 15;
43
44         pr_info("out-of-bounds to left\n");
45         ptr = kmalloc(size, GFP_KERNEL);
46         if (!ptr) {
47                 pr_err("Allocation failed\n");
48                 return;
49         }
50
51         *ptr = *(ptr - 1);
52         kfree(ptr);
53 }
54
55 static noinline void __init kmalloc_node_oob_right(void)
56 {
57         char *ptr;
58         size_t size = 4096;
59
60         pr_info("kmalloc_node(): out-of-bounds to right\n");
61         ptr = kmalloc_node(size, GFP_KERNEL, 0);
62         if (!ptr) {
63                 pr_err("Allocation failed\n");
64                 return;
65         }
66
67         ptr[size] = 0;
68         kfree(ptr);
69 }
70
71 #ifdef CONFIG_SLUB
72 static noinline void __init kmalloc_pagealloc_oob_right(void)
73 {
74         char *ptr;
75         size_t size = KMALLOC_MAX_CACHE_SIZE + 10;
76
77         /* Allocate a chunk that does not fit into a SLUB cache to trigger
78          * the page allocator fallback.
79          */
80         pr_info("kmalloc pagealloc allocation: out-of-bounds to right\n");
81         ptr = kmalloc(size, GFP_KERNEL);
82         if (!ptr) {
83                 pr_err("Allocation failed\n");
84                 return;
85         }
86
87         ptr[size] = 0;
88         kfree(ptr);
89 }
90 #endif
91
92 static noinline void __init kmalloc_large_oob_right(void)
93 {
94         char *ptr;
95         size_t size = KMALLOC_MAX_CACHE_SIZE - 256;
96         /* Allocate a chunk that is large enough, but still fits into a slab
97          * and does not trigger the page allocator fallback in SLUB.
98          */
99         pr_info("kmalloc large allocation: out-of-bounds to right\n");
100         ptr = kmalloc(size, GFP_KERNEL);
101         if (!ptr) {
102                 pr_err("Allocation failed\n");
103                 return;
104         }
105
106         ptr[size] = 0;
107         kfree(ptr);
108 }
109
110 static noinline void __init kmalloc_oob_krealloc_more(void)
111 {
112         char *ptr1, *ptr2;
113         size_t size1 = 17;
114         size_t size2 = 19;
115
116         pr_info("out-of-bounds after krealloc more\n");
117         ptr1 = kmalloc(size1, GFP_KERNEL);
118         ptr2 = krealloc(ptr1, size2, GFP_KERNEL);
119         if (!ptr1 || !ptr2) {
120                 pr_err("Allocation failed\n");
121                 kfree(ptr1);
122                 return;
123         }
124
125         ptr2[size2] = 'x';
126         kfree(ptr2);
127 }
128
129 static noinline void __init kmalloc_oob_krealloc_less(void)
130 {
131         char *ptr1, *ptr2;
132         size_t size1 = 17;
133         size_t size2 = 15;
134
135         pr_info("out-of-bounds after krealloc less\n");
136         ptr1 = kmalloc(size1, GFP_KERNEL);
137         ptr2 = krealloc(ptr1, size2, GFP_KERNEL);
138         if (!ptr1 || !ptr2) {
139                 pr_err("Allocation failed\n");
140                 kfree(ptr1);
141                 return;
142         }
143         ptr2[size2] = 'x';
144         kfree(ptr2);
145 }
146
147 static noinline void __init kmalloc_oob_16(void)
148 {
149         struct {
150                 u64 words[2];
151         } *ptr1, *ptr2;
152
153         pr_info("kmalloc out-of-bounds for 16-bytes access\n");
154         ptr1 = kmalloc(sizeof(*ptr1) - 3, GFP_KERNEL);
155         ptr2 = kmalloc(sizeof(*ptr2), GFP_KERNEL);
156         if (!ptr1 || !ptr2) {
157                 pr_err("Allocation failed\n");
158                 kfree(ptr1);
159                 kfree(ptr2);
160                 return;
161         }
162         *ptr1 = *ptr2;
163         kfree(ptr1);
164         kfree(ptr2);
165 }
166
167 static noinline void __init kmalloc_oob_memset_2(void)
168 {
169         char *ptr;
170         size_t size = 8;
171
172         pr_info("out-of-bounds in memset2\n");
173         ptr = kmalloc(size, GFP_KERNEL);
174         if (!ptr) {
175                 pr_err("Allocation failed\n");
176                 return;
177         }
178
179         memset(ptr+7, 0, 2);
180         kfree(ptr);
181 }
182
183 static noinline void __init kmalloc_oob_memset_4(void)
184 {
185         char *ptr;
186         size_t size = 8;
187
188         pr_info("out-of-bounds in memset4\n");
189         ptr = kmalloc(size, GFP_KERNEL);
190         if (!ptr) {
191                 pr_err("Allocation failed\n");
192                 return;
193         }
194
195         memset(ptr+5, 0, 4);
196         kfree(ptr);
197 }
198
199
200 static noinline void __init kmalloc_oob_memset_8(void)
201 {
202         char *ptr;
203         size_t size = 8;
204
205         pr_info("out-of-bounds in memset8\n");
206         ptr = kmalloc(size, GFP_KERNEL);
207         if (!ptr) {
208                 pr_err("Allocation failed\n");
209                 return;
210         }
211
212         memset(ptr+1, 0, 8);
213         kfree(ptr);
214 }
215
216 static noinline void __init kmalloc_oob_memset_16(void)
217 {
218         char *ptr;
219         size_t size = 16;
220
221         pr_info("out-of-bounds in memset16\n");
222         ptr = kmalloc(size, GFP_KERNEL);
223         if (!ptr) {
224                 pr_err("Allocation failed\n");
225                 return;
226         }
227
228         memset(ptr+1, 0, 16);
229         kfree(ptr);
230 }
231
232 static noinline void __init kmalloc_oob_in_memset(void)
233 {
234         char *ptr;
235         size_t size = 666;
236
237         pr_info("out-of-bounds in memset\n");
238         ptr = kmalloc(size, GFP_KERNEL);
239         if (!ptr) {
240                 pr_err("Allocation failed\n");
241                 return;
242         }
243
244         memset(ptr, 0, size+5);
245         kfree(ptr);
246 }
247
248 static noinline void __init kmalloc_uaf(void)
249 {
250         char *ptr;
251         size_t size = 10;
252
253         pr_info("use-after-free\n");
254         ptr = kmalloc(size, GFP_KERNEL);
255         if (!ptr) {
256                 pr_err("Allocation failed\n");
257                 return;
258         }
259
260         kfree(ptr);
261         *(ptr + 8) = 'x';
262 }
263
264 static noinline void __init kmalloc_uaf_memset(void)
265 {
266         char *ptr;
267         size_t size = 33;
268
269         pr_info("use-after-free in memset\n");
270         ptr = kmalloc(size, GFP_KERNEL);
271         if (!ptr) {
272                 pr_err("Allocation failed\n");
273                 return;
274         }
275
276         kfree(ptr);
277         memset(ptr, 0, size);
278 }
279
280 static noinline void __init kmalloc_uaf2(void)
281 {
282         char *ptr1, *ptr2;
283         size_t size = 43;
284
285         pr_info("use-after-free after another kmalloc\n");
286         ptr1 = kmalloc(size, GFP_KERNEL);
287         if (!ptr1) {
288                 pr_err("Allocation failed\n");
289                 return;
290         }
291
292         kfree(ptr1);
293         ptr2 = kmalloc(size, GFP_KERNEL);
294         if (!ptr2) {
295                 pr_err("Allocation failed\n");
296                 return;
297         }
298
299         ptr1[40] = 'x';
300         if (ptr1 == ptr2)
301                 pr_err("Could not detect use-after-free: ptr1 == ptr2\n");
302         kfree(ptr2);
303 }
304
305 static noinline void __init kmem_cache_oob(void)
306 {
307         char *p;
308         size_t size = 200;
309         struct kmem_cache *cache = kmem_cache_create("test_cache",
310                                                 size, 0,
311                                                 0, NULL);
312         if (!cache) {
313                 pr_err("Cache allocation failed\n");
314                 return;
315         }
316         pr_info("out-of-bounds in kmem_cache_alloc\n");
317         p = kmem_cache_alloc(cache, GFP_KERNEL);
318         if (!p) {
319                 pr_err("Allocation failed\n");
320                 kmem_cache_destroy(cache);
321                 return;
322         }
323
324         *p = p[size];
325         kmem_cache_free(cache, p);
326         kmem_cache_destroy(cache);
327 }
328
329 static char global_array[10];
330
331 static noinline void __init kasan_global_oob(void)
332 {
333         volatile int i = 3;
334         char *p = &global_array[ARRAY_SIZE(global_array) + i];
335
336         pr_info("out-of-bounds global variable\n");
337         *(volatile char *)p;
338 }
339
340 static noinline void __init kasan_stack_oob(void)
341 {
342         char stack_array[10];
343         volatile int i = 0;
344         char *p = &stack_array[ARRAY_SIZE(stack_array) + i];
345
346         pr_info("out-of-bounds on stack\n");
347         *(volatile char *)p;
348 }
349
350 static noinline void __init ksize_unpoisons_memory(void)
351 {
352         char *ptr;
353         size_t size = 123, real_size = size;
354
355         pr_info("ksize() unpoisons the whole allocated chunk\n");
356         ptr = kmalloc(size, GFP_KERNEL);
357         if (!ptr) {
358                 pr_err("Allocation failed\n");
359                 return;
360         }
361         real_size = ksize(ptr);
362         /* This access doesn't trigger an error. */
363         ptr[size] = 'x';
364         /* This one does. */
365         ptr[real_size] = 'y';
366         kfree(ptr);
367 }
368
369 static noinline void __init copy_user_test(void)
370 {
371         char *kmem;
372         char __user *usermem;
373         size_t size = 10;
374         int unused;
375
376         kmem = kmalloc(size, GFP_KERNEL);
377         if (!kmem)
378                 return;
379
380         usermem = (char __user *)vm_mmap(NULL, 0, PAGE_SIZE,
381                             PROT_READ | PROT_WRITE | PROT_EXEC,
382                             MAP_ANONYMOUS | MAP_PRIVATE, 0);
383         if (IS_ERR(usermem)) {
384                 pr_err("Failed to allocate user memory\n");
385                 kfree(kmem);
386                 return;
387         }
388
389         pr_info("out-of-bounds in copy_from_user()\n");
390         unused = copy_from_user(kmem, usermem, size + 1);
391
392         pr_info("out-of-bounds in copy_to_user()\n");
393         unused = copy_to_user(usermem, kmem, size + 1);
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_inatomic()\n");
402         unused = __copy_from_user_inatomic(kmem, usermem, size + 1);
403
404         pr_info("out-of-bounds in __copy_to_user_inatomic()\n");
405         unused = __copy_to_user_inatomic(usermem, kmem, size + 1);
406
407         pr_info("out-of-bounds in strncpy_from_user()\n");
408         unused = strncpy_from_user(kmem, usermem, size + 1);
409
410         vm_munmap((unsigned long)usermem, PAGE_SIZE);
411         kfree(kmem);
412 }
413
414 static int __init kmalloc_tests_init(void)
415 {
416         kmalloc_oob_right();
417         kmalloc_oob_left();
418         kmalloc_node_oob_right();
419 #ifdef CONFIG_SLUB
420         kmalloc_pagealloc_oob_right();
421 #endif
422         kmalloc_large_oob_right();
423         kmalloc_oob_krealloc_more();
424         kmalloc_oob_krealloc_less();
425         kmalloc_oob_16();
426         kmalloc_oob_in_memset();
427         kmalloc_oob_memset_2();
428         kmalloc_oob_memset_4();
429         kmalloc_oob_memset_8();
430         kmalloc_oob_memset_16();
431         kmalloc_uaf();
432         kmalloc_uaf_memset();
433         kmalloc_uaf2();
434         kmem_cache_oob();
435         kasan_stack_oob();
436         kasan_global_oob();
437         ksize_unpoisons_memory();
438         copy_user_test();
439         return -EAGAIN;
440 }
441
442 module_init(kmalloc_tests_init);
443 MODULE_LICENSE("GPL");