]> git.saurik.com Git - apple/xnu.git/blob - san/kasan-test.c
xnu-7195.81.3.tar.gz
[apple/xnu.git] / san / kasan-test.c
1 /*
2 * Copyright (c) 2016 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 #include <stdint.h>
30 #include <string.h>
31 #include <vm/vm_map.h>
32 #include <kern/assert.h>
33 #include <kern/locks.h>
34 #include <kern/kalloc.h>
35 #include <kern/simple_lock.h>
36 #include <kern/debug.h>
37 #include <kern/thread.h>
38 #include <mach/mach_vm.h>
39 #include <mach/vm_param.h>
40 #include <libkern/libkern.h>
41 #include <libkern/kernel_mach_header.h>
42 #include <sys/queue.h>
43 #include <kasan.h>
44 #include <kasan_internal.h>
45 #include <memintrinsics.h>
46
47 #define STATIC_ARRAY_SZ 66
48 #define STACK_ARRAY_SZ 9
49 #define BUFSZ 34
50 #define LBUFSZ 255
51
52 enum {
53 TEST_PASS,
54 TEST_FAIL_NOFAULT,
55 TEST_FAIL_BADFAULT,
56 TEST_SETUP_FAIL = 1,
57 TEST_INVALID,
58 TEST_UNKNOWN
59 };
60
61 unsigned long static_array[STATIC_ARRAY_SZ];
62
63 static jmp_buf jbuf;
64 static volatile int in_test = 0;
65
66 struct kasan_test {
67 int (* func)(struct kasan_test *);
68 void (* cleanup)(struct kasan_test *);
69 const char *name;
70 int result;
71 void *data;
72 size_t datasz;
73 };
74
75 #define TEST_BARRIER() do { __asm__ __volatile__ ("" ::: "memory"); } while(0)
76 #define TEST_START(t) do { t->result = 1; TEST_BARRIER(); } while (0)
77 #define TEST_FAULT(t) do { TEST_BARRIER(); t->result = 0; TEST_BARRIER(); } while (0)
78 #define TEST_NOFAULT(t) do { TEST_BARRIER(); t->result = 1; TEST_BARRIER(); } while (0)
79 #define TEST_DONE(t, res) do { t->result = (res); kasan_handle_test(); } while (0)
80 #define DECLARE_TEST(f, s) { .func = f, .name = s }
81 #define DECLARE_TEST3(f, c, s) { .func = f, .cleanup = c, .name = s }
82
83 static void
84 heap_cleanup(struct kasan_test *t)
85 {
86 if (t->data) {
87 kfree(t->data, t->datasz);
88 t->data = NULL;
89 }
90 }
91
92 static int
93 test_global_overflow(struct kasan_test __unused *t)
94 {
95 int i;
96 /* rookie error */
97 for (i = 0; i <= STATIC_ARRAY_SZ; i++) {
98 static_array[i] = i;
99 }
100 return 0;
101 }
102
103 static int
104 test_heap_underflow(struct kasan_test __unused *t)
105 {
106 uint8_t *x = kalloc(BUFSZ);
107 if (!x) {
108 return 1;
109 }
110 t->datasz = BUFSZ;
111 t->data = x;
112 x[-1] = 0x12;
113 return 0;
114 }
115
116 static int
117 test_heap_overflow(struct kasan_test __unused *t)
118 {
119 uint8_t *x = kalloc(BUFSZ);
120 if (!x) {
121 return 1;
122 }
123 t->datasz = BUFSZ;
124 t->data = x;
125 x[BUFSZ] = 0x11;
126 return 0;
127 }
128
129 static int
130 test_heap_uaf(struct kasan_test __unused *t)
131 {
132 uint8_t *x = kalloc(LBUFSZ);
133 if (!x) {
134 return 1;
135 }
136 kfree(x, LBUFSZ);
137 x[0] = 0x10;
138 return 0;
139 }
140
141 static int
142 test_heap_inval_free(struct kasan_test __unused *t)
143 {
144 int x;
145 int *ptr = &x;
146 kfree(ptr, BUFSZ);
147 return 0;
148 }
149
150 static int
151 test_heap_double_free(struct kasan_test *t)
152 {
153 TEST_START(t);
154
155 uint8_t *x = kalloc(BUFSZ);
156 if (!x) {
157 return 1;
158 }
159 kfree(x, BUFSZ);
160
161 TEST_FAULT(t);
162 kfree(x, BUFSZ);
163
164 return 0;
165 }
166
167 static int
168 test_heap_small_free(struct kasan_test *t)
169 {
170 TEST_START(t);
171
172 uint8_t *x = kalloc(BUFSZ);
173 if (!x) {
174 return 1;
175 }
176 t->datasz = BUFSZ;
177 t->data = x;
178
179 TEST_FAULT(t);
180 kfree(x, BUFSZ - 2);
181 t->data = NULL;
182 t->datasz = 0;
183
184 return 0;
185 }
186
187 static int
188 test_stack_overflow(struct kasan_test *t)
189 {
190 TEST_START(t);
191
192 uint8_t i;
193 volatile uint8_t a[STACK_ARRAY_SZ];
194
195 for (i = 0; i < STACK_ARRAY_SZ; i++) {
196 a[i] = i;
197 }
198
199 TEST_FAULT(t);
200 a[i] = i; /* rookie error */
201 TEST_NOFAULT(t);
202
203 TEST_BARRIER();
204
205 return !(a[0] == 0);
206 }
207
208 static int
209 test_stack_underflow(struct kasan_test *t)
210 {
211 TEST_START(t);
212
213 long idx;
214 uint8_t a[STACK_ARRAY_SZ];
215
216 __nosan_memset(a, 0, STACK_ARRAY_SZ);
217
218 /* generate a negative index without the compiler noticing */
219 #if __x86_64__
220 __asm__ __volatile__ ("movq $-1, %0" : "=r"(idx) :: "memory");
221 #else
222 __asm__ __volatile__ ("mov %0, #-1" : "=r"(idx) :: "memory");
223 #endif
224
225 TEST_FAULT(t);
226 a[idx] = 0xbd;
227 TEST_NOFAULT(t);
228
229 TEST_BARRIER();
230 return a[0] == 0;
231 }
232
233 static int
234 test_memcpy(struct kasan_test *t)
235 {
236 TEST_START(t);
237 uint8_t a1[STACK_ARRAY_SZ];
238 uint8_t a2[STACK_ARRAY_SZ];
239
240 /* should work */
241 memcpy(a1, a2, STACK_ARRAY_SZ);
242
243 TEST_BARRIER();
244
245 /* should fail */
246 TEST_FAULT(t);
247 memcpy(a2, a1, STACK_ARRAY_SZ + 1);
248 TEST_NOFAULT(t);
249
250 return 0;
251 }
252
253 static int
254 test_memmove(struct kasan_test *t)
255 {
256 TEST_START(t);
257 uint8_t a1[STACK_ARRAY_SZ];
258 uint8_t a2[STACK_ARRAY_SZ];
259
260 /* should work */
261 memmove(a1, a2, STACK_ARRAY_SZ);
262
263 TEST_BARRIER();
264
265 /* should fail */
266 TEST_FAULT(t);
267 memmove(a2, a1, STACK_ARRAY_SZ + 1);
268 TEST_NOFAULT(t);
269
270 return 0;
271 }
272
273 static int
274 test_bcopy(struct kasan_test *t)
275 {
276 TEST_START(t);
277 uint8_t a1[STACK_ARRAY_SZ];
278 uint8_t a2[STACK_ARRAY_SZ];
279
280 /* should work */
281 bcopy(a1, a2, STACK_ARRAY_SZ);
282
283 TEST_BARRIER();
284
285 /* should fail */
286 TEST_FAULT(t);
287 bcopy(a2, a1, STACK_ARRAY_SZ + 1);
288 TEST_NOFAULT(t);
289
290 return 0;
291 }
292
293 static int
294 test_memset(struct kasan_test *t)
295 {
296 TEST_START(t);
297 uint8_t a1[STACK_ARRAY_SZ];
298
299 /* should work */
300 memset(a1, 'e', STACK_ARRAY_SZ);
301
302 TEST_BARRIER();
303
304 /* should fail */
305 TEST_FAULT(t);
306 memset(a1, 'f', STACK_ARRAY_SZ + 1);
307 TEST_NOFAULT(t);
308
309 return 0;
310 }
311
312 static int
313 test_memcmp(struct kasan_test *t)
314 {
315 TEST_START(t);
316 uint8_t *a1;
317 uint8_t *a2;
318
319 a1 = kalloc(STACK_ARRAY_SZ);
320 if (!a1) {
321 return 1;
322 }
323 a2 = kalloc(STACK_ARRAY_SZ + 1);
324 if (!a2) {
325 return 1;
326 }
327
328 /* should work */
329 memcmp(a1, a2, STACK_ARRAY_SZ);
330 memcmp(a1, a2 + 1, STACK_ARRAY_SZ);
331
332 TEST_BARRIER();
333
334 /* should fail */
335 TEST_FAULT(t);
336 memcmp(a1, a2, STACK_ARRAY_SZ + 1);
337 TEST_NOFAULT(t);
338
339 return 0;
340 }
341
342 static int
343 test_bcmp(struct kasan_test *t)
344 {
345 TEST_START(t);
346 uint8_t *a1;
347 uint8_t *a2;
348
349 a1 = kalloc(STACK_ARRAY_SZ);
350 if (!a1) {
351 return 1;
352 }
353 a2 = kalloc(STACK_ARRAY_SZ + 1);
354 if (!a2) {
355 return 1;
356 }
357
358 /* should work */
359 bcmp(a1, a2, STACK_ARRAY_SZ);
360 bcmp(a1, a2 + 1, STACK_ARRAY_SZ);
361
362 TEST_BARRIER();
363
364 /* should fail */
365 TEST_FAULT(t);
366 bcmp(a1, a2, STACK_ARRAY_SZ + 1);
367 TEST_NOFAULT(t);
368
369 return 0;
370 }
371
372 static int
373 test_bzero(struct kasan_test *t)
374 {
375 TEST_START(t);
376 uint8_t a1[STACK_ARRAY_SZ];
377
378 /* should work */
379 bzero(a1, STACK_ARRAY_SZ);
380
381 TEST_BARRIER();
382
383 /* should fail */
384 TEST_FAULT(t);
385 bzero(a1, STACK_ARRAY_SZ + 1);
386 TEST_NOFAULT(t);
387
388 return 0;
389 }
390
391 static int
392 test_strlcpy(struct kasan_test *t)
393 {
394 TEST_START(t);
395 char a1[8];
396
397 /* should not fault */
398 strlcpy(a1, "small", 8);
399 strlcpy(a1, "looooonnnnggg", 8);
400
401 TEST_FAULT(t);
402 strlcpy(a1, "looooooooonnnnggg", 9);
403 TEST_NOFAULT(t);
404
405 return 0;
406 }
407
408 static int
409 test_strncpy(struct kasan_test *t)
410 {
411 TEST_START(t);
412 char a1[9];
413
414 /* should not fault */
415 strncpy(a1, "small", 9);
416 strncpy(a1, "looooonnnnggg", 9);
417
418 TEST_FAULT(t);
419 strncpy(a1, "looooonnnnggg", 10);
420 TEST_NOFAULT(t);
421
422 return a1[0] != 'l';
423 }
424
425 static int
426 test_strlcat(struct kasan_test *t)
427 {
428 TEST_START(t);
429 char a1[9] = {};
430
431 /* should not fault */
432 strlcat(a1, "abcd", 9);
433 strlcat(a1, "efgh", 9);
434 strlcat(a1, "ijkl", 9);
435 a1[0] = '\0';
436 strlcat(a1, "looooonnnnggg", 9);
437
438 a1[0] = '\0';
439 TEST_FAULT(t);
440 strlcat(a1, "looooonnnnggg", 10);
441 TEST_NOFAULT(t);
442
443 return a1[0] != 'l';
444 }
445
446 static int
447 test_strncat(struct kasan_test *t)
448 {
449 TEST_START(t);
450 char a1[9] = {};
451
452 /* should not fault */
453 strncat(a1, "abcd", 4);
454 strncat(a1, "efgh", 4);
455
456 TEST_FAULT(t);
457 strncat(a1, "i", 1);
458 TEST_NOFAULT(t);
459
460 return a1[0] != 'a';
461 }
462
463 /* we ignore the top *two* frames in backtrace - so add an extra one */
464 static int OS_NOINLINE
465 test_blacklist_helper(void)
466 {
467 return kasan_is_blacklisted(TYPE_TEST);
468 }
469
470 static int OS_NOINLINE
471 test_blacklist(struct kasan_test *t)
472 {
473 TEST_START(t);
474 int res = (int)!test_blacklist_helper();
475 TEST_DONE(t, res);
476 return 0;
477 }
478
479 static int OS_NOINLINE
480 test_blacklist_str(struct kasan_test *t)
481 {
482 TEST_START(t);
483 char a1[8];
484
485 bcopy("123456", a1, 8);
486
487 TEST_DONE(t, 0); /* success */
488 return 0;
489 }
490
491 #if 0
492 static int
493 test_strnlen(struct kasan_test *t)
494 {
495 TEST_START(t);
496 const char *a1 = "abcdef";
497
498 /* should not fault */
499 if (strnlen(a1, 6) != 6) {
500 return 1;
501 }
502 if (strnlen(a1, 7) != 6) {
503 return 1;
504 }
505
506 TEST_FAULT(t);
507 if (strnlen(a1, 8) != 6) {
508 return 1;
509 }
510 TEST_NOFAULT(t);
511
512 return a1[0] != 'a';
513 }
514 #endif
515
516 static void OS_NOINLINE
517 force_fakestack(char *x)
518 {
519 __asm__ __volatile__ ("" :: "r" (x) : "memory");
520 }
521
522 OS_NOINLINE
523 static int
524 test_fakestack_helper(struct kasan_test *t, char *x)
525 {
526 TEST_START(t);
527
528 x[0] = 0x55;
529
530 /* ensure that 'x' is on the fakestack */
531 uintptr_t base = dtrace_get_kernel_stack(current_thread());
532 uintptr_t p = (uintptr_t)x;
533 if (p >= base && p < base + kernel_stack_size) {
534 return 1;
535 }
536
537 __asan_handle_no_return();
538
539 /* x better still be accessible */
540 TEST_NOFAULT(t);
541 if (x[0] != 0x55) {
542 TEST_DONE(t, 1);
543 }
544
545 TEST_DONE(t, 0);
546 return 0;
547 }
548
549 static int
550 test_fakestack(struct kasan_test *t)
551 {
552 char x[8];
553 if (!fakestack_enabled) {
554 return 1;
555 }
556 force_fakestack(x);
557 return test_fakestack_helper(t, x);
558 }
559
560 int *uaf_ptr;
561 static int * NOINLINE
562 stack_uaf_helper(void)
563 {
564 int x;
565 uaf_ptr = &x;
566 return uaf_ptr;
567 }
568
569 static int
570 test_stack_uaf(struct kasan_test __unused *t)
571 {
572 int *x = stack_uaf_helper();
573 *x = 0xb4d;
574 TEST_BARRIER();
575 return !(*x == 0xb4d);
576 }
577
578 static struct kasan_test xnu_tests[] = {
579 DECLARE_TEST(NULL, NULL),
580 DECLARE_TEST(test_global_overflow, "Global overflow"),
581 DECLARE_TEST3(test_heap_underflow, heap_cleanup, "Heap underflow"),
582 DECLARE_TEST3(test_heap_overflow, heap_cleanup, "Heap overflow"),
583 DECLARE_TEST(test_heap_uaf, "Heap use-after-free"),
584 DECLARE_TEST(test_heap_inval_free, "Heap invalid free"),
585 DECLARE_TEST(test_heap_double_free, "Heap double free"),
586 DECLARE_TEST3(test_heap_small_free, heap_cleanup, "Heap small free"),
587 DECLARE_TEST(test_stack_overflow, "Stack overflow"),
588 DECLARE_TEST(test_stack_underflow, "Stack underflow"),
589 DECLARE_TEST(test_stack_uaf, "Stack use-after-return"),
590 DECLARE_TEST(test_memcpy, "memcpy"),
591 DECLARE_TEST(test_memmove, "memmmove"),
592 DECLARE_TEST(test_bcopy, "bcopy"),
593 DECLARE_TEST(test_memset, "memset"),
594 DECLARE_TEST(test_memcmp, "memcmp"),
595 DECLARE_TEST(test_bcmp, "bcmp"),
596 DECLARE_TEST(test_bzero, "bzero"),
597 DECLARE_TEST(test_strlcpy, "strlcpy"),
598 DECLARE_TEST(test_strlcat, "strlcat"),
599 DECLARE_TEST(test_strncpy, "strncpy"),
600 DECLARE_TEST(test_strncat, "strncat"),
601 DECLARE_TEST(test_blacklist, "blacklist"),
602 DECLARE_TEST(test_blacklist_str, "blacklist_str"),
603 DECLARE_TEST(test_fakestack, "fakestack"),
604 // DECLARE_TEST(test_strnlen, "strnlen"),
605 };
606 static int num_xnutests = sizeof(xnu_tests) / sizeof(xnu_tests[0]);
607
608 static int
609 kasan_run_test(struct kasan_test *test_list, int testno, int fail)
610 {
611 int status = TEST_UNKNOWN;
612 struct kasan_test *t = &test_list[testno];
613
614 if (testno < 0 || testno >= num_xnutests || !t->func) {
615 printf("KASan: test.%02d INVALID\n", testno);
616 return TEST_INVALID;
617 }
618
619 // printf("KASan: test.%02d RUNNING (%s)\n", testno, t->name);
620
621 if (!fail) {
622 in_test = 1;
623 }
624
625 if (_setjmp(jbuf) == 0) {
626 t->result = 0;
627 int ret = t->func(t);
628 if (ret) {
629 printf("KASan: test.%02d SETUP FAIL (%s)\n", testno, t->name);
630 status = ret;
631 } else {
632 /* did not fault when it should have */
633 printf("KASan: test.%02d FAIL (%s)\n", testno, t->name);
634 status = TEST_FAIL_NOFAULT;
635 }
636 } else {
637 if (t->result) {
638 /* faulted, but at the wrong place */
639 printf("KASan: test.%02d FAIL %d (%s)\n", testno, t->result, t->name);
640 status = TEST_FAIL_BADFAULT;
641 } else {
642 printf("KASan: test.%02d PASS (%s)\n", testno, t->name);
643 status = TEST_PASS;
644 }
645 }
646 in_test = 0;
647 if (t->cleanup) {
648 t->cleanup(t);
649 }
650
651 return status;
652 }
653
654 void
655 kasan_test(int testno, int fail)
656 {
657 int i = 1;
658 int pass = 0, total = 0;
659 int ret;
660
661 if (testno == -1) {
662 /* shorthand for all tests */
663 testno = (1U << (num_xnutests - 1)) - 1;
664 }
665
666 while (testno) {
667 if (testno & 0x1) {
668 ret = kasan_run_test(xnu_tests, i, fail);
669 if (ret == TEST_PASS) {
670 pass++;
671 }
672 if (ret != TEST_INVALID) {
673 total++;
674 }
675 }
676
677 i++;
678 testno >>= 1;
679 }
680 printf("KASan: TEST SUMMARY %d/%d passed\n", pass, total);
681 }
682
683 void
684 kasan_handle_test(void)
685 {
686 if (in_test) {
687 _longjmp(jbuf, 1);
688 /* NOTREACHED */
689 }
690 }
691
692 void
693 __kasan_runtests(struct kasan_test *kext_tests, int numtests)
694 {
695 int i;
696 for (i = 0; i < numtests; i++) {
697 kasan_run_test(kext_tests, i, 0);
698 }
699 }