]> git.saurik.com Git - apple/xnu.git/blob - san/kasan-test.c
xnu-4570.61.1.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 <mach/mach_vm.h>
38 #include <mach/vm_param.h>
39 #include <libkern/libkern.h>
40 #include <libkern/kernel_mach_header.h>
41 #include <sys/queue.h>
42 #include <kasan.h>
43 #include <kasan_internal.h>
44 #include <memintrinsics.h>
45
46 #define STATIC_ARRAY_SZ 66
47 #define STACK_ARRAY_SZ 9
48 #define BUFSZ 34
49 #define LBUFSZ 255
50
51 enum {
52 TEST_PASS,
53 TEST_FAIL_NOFAULT,
54 TEST_FAIL_BADFAULT,
55 TEST_SETUP_FAIL = 1,
56 TEST_INVALID,
57 TEST_UNKNOWN
58 };
59
60 unsigned long static_array[STATIC_ARRAY_SZ];
61
62 static jmp_buf jbuf;
63 static volatile int in_test = 0;
64
65 struct kasan_test {
66 int (* func)(struct kasan_test *);
67 void (* cleanup)(struct kasan_test *);
68 const char *name;
69 int result;
70 void *data;
71 size_t datasz;
72 };
73
74 #define TEST_BARRIER() do { __asm__ __volatile__ ("" ::: "memory"); } while(0)
75 #define TEST_START(t) do { t->result = 1; TEST_BARRIER(); } while (0)
76 #define TEST_FAULT(t) do { TEST_BARRIER(); t->result = 0; TEST_BARRIER(); } while (0)
77 #define TEST_NOFAULT(t) do { TEST_BARRIER(); t->result = 1; TEST_BARRIER(); } while (0)
78 #define TEST_DONE(t,res) do { t->result = (res); kasan_handle_test(); } while (0)
79 #define DECLARE_TEST(f,s) { .func = f, .name = s }
80 #define DECLARE_TEST3(f,c,s) { .func = f, .cleanup = c, .name = s }
81
82 static void heap_cleanup(struct kasan_test *t)
83 {
84 if (t->data) {
85 kfree(t->data, t->datasz);
86 t->data = NULL;
87 }
88 }
89
90 static int test_global_overflow(struct kasan_test __unused *t)
91 {
92 int i;
93 /* rookie error */
94 for (i = 0; i <= STATIC_ARRAY_SZ; i++) {
95 static_array[i] = i;
96 }
97 return 0;
98 }
99
100 static int test_heap_underflow(struct kasan_test __unused *t)
101 {
102 uint8_t *x = kalloc(BUFSZ);
103 if (!x) {
104 return 1;
105 }
106 t->datasz = BUFSZ;
107 t->data = x;
108 x[-1] = 0x12;
109 return 0;
110 }
111
112 static int test_heap_overflow(struct kasan_test __unused *t)
113 {
114 uint8_t *x = kalloc(BUFSZ);
115 if (!x) {
116 return 1;
117 }
118 t->datasz = BUFSZ;
119 t->data = x;
120 x[BUFSZ] = 0x11;
121 return 0;
122 }
123
124 static int test_heap_uaf(struct kasan_test __unused *t)
125 {
126 uint8_t *x = kalloc(LBUFSZ);
127 if (!x) {
128 return 1;
129 }
130 kfree(x, LBUFSZ);
131 x[0] = 0x10;
132 return 0;
133 }
134
135 static int test_heap_inval_free(struct kasan_test __unused *t)
136 {
137 int x;
138 kfree(&x, BUFSZ);
139 return 0;
140 }
141
142 static int test_heap_double_free(struct kasan_test *t)
143 {
144 TEST_START(t);
145
146 uint8_t *x = kalloc(BUFSZ);
147 if (!x) {
148 return 1;
149 }
150 kfree(x, BUFSZ);
151
152 TEST_FAULT(t);
153 kfree(x, BUFSZ);
154
155 return 0;
156 }
157
158 static int test_heap_small_free(struct kasan_test *t)
159 {
160 TEST_START(t);
161
162 uint8_t *x = kalloc(BUFSZ);
163 if (!x) {
164 return 1;
165 }
166 t->datasz = BUFSZ;
167 t->data = x;
168
169 TEST_FAULT(t);
170 kfree(x, BUFSZ-2);
171 t->data = NULL;
172 t->datasz = 0;
173
174 return 0;
175 }
176
177 static int test_stack_overflow(struct kasan_test *t)
178 {
179 TEST_START(t);
180
181 int i;
182 volatile uint8_t a[STACK_ARRAY_SZ];
183
184 for (i = 0; i < STACK_ARRAY_SZ; i++) {
185 a[i] = i;
186 }
187
188 TEST_FAULT(t);
189 a[i] = i; /* rookie error */
190 TEST_NOFAULT(t);
191
192 TEST_BARRIER();
193
194 return !(a[0] == 0);
195 }
196
197 static int test_stack_underflow(struct kasan_test *t)
198 {
199 TEST_START(t);
200
201 long idx;
202 uint8_t a[STACK_ARRAY_SZ];
203
204 __nosan_memset(a, 0, STACK_ARRAY_SZ);
205
206 /* generate a negative index without the compiler noticing */
207 #if __x86_64__
208 __asm__ __volatile__("movq $-1, %0" : "=r"(idx) :: "memory");
209 #else
210 __asm__ __volatile__("mov %0, #-1" : "=r"(idx) :: "memory");
211 #endif
212
213 TEST_FAULT(t);
214 a[idx] = 0xbd;
215 TEST_NOFAULT(t);
216
217 TEST_BARRIER();
218 return (a[0] == 0);
219 }
220
221 static int test_memcpy(struct kasan_test *t)
222 {
223 TEST_START(t);
224 uint8_t a1[STACK_ARRAY_SZ];
225 uint8_t a2[STACK_ARRAY_SZ];
226
227 /* should work */
228 memcpy(a1, a2, STACK_ARRAY_SZ);
229
230 TEST_BARRIER();
231
232 /* should fail */
233 TEST_FAULT(t);
234 memcpy(a2, a1, STACK_ARRAY_SZ+1);
235 TEST_NOFAULT(t);
236
237 return 0;
238 }
239
240 static int test_memmove(struct kasan_test *t)
241 {
242 TEST_START(t);
243 uint8_t a1[STACK_ARRAY_SZ];
244 uint8_t a2[STACK_ARRAY_SZ];
245
246 /* should work */
247 memmove(a1, a2, STACK_ARRAY_SZ);
248
249 TEST_BARRIER();
250
251 /* should fail */
252 TEST_FAULT(t);
253 memmove(a2, a1, STACK_ARRAY_SZ+1);
254 TEST_NOFAULT(t);
255
256 return 0;
257 }
258
259 static int test_bcopy(struct kasan_test *t)
260 {
261 TEST_START(t);
262 uint8_t a1[STACK_ARRAY_SZ];
263 uint8_t a2[STACK_ARRAY_SZ];
264
265 /* should work */
266 bcopy(a1, a2, STACK_ARRAY_SZ);
267
268 TEST_BARRIER();
269
270 /* should fail */
271 TEST_FAULT(t);
272 bcopy(a2, a1, STACK_ARRAY_SZ+1);
273 TEST_NOFAULT(t);
274
275 return 0;
276 }
277
278 static int test_memset(struct kasan_test *t)
279 {
280 TEST_START(t);
281 uint8_t a1[STACK_ARRAY_SZ];
282
283 /* should work */
284 memset(a1, 'e', STACK_ARRAY_SZ);
285
286 TEST_BARRIER();
287
288 /* should fail */
289 TEST_FAULT(t);
290 memset(a1, 'f', STACK_ARRAY_SZ+1);
291 TEST_NOFAULT(t);
292
293 return 0;
294 }
295
296 static int test_memcmp(struct kasan_test *t)
297 {
298 TEST_START(t);
299 uint8_t *a1;
300 uint8_t *a2;
301
302 a1 = kalloc(STACK_ARRAY_SZ);
303 if (!a1)
304 return 1;
305 a2 = kalloc(STACK_ARRAY_SZ+1);
306 if (!a2)
307 return 1;
308
309 /* should work */
310 memcmp(a1, a2, STACK_ARRAY_SZ);
311 memcmp(a1, a2+1, STACK_ARRAY_SZ);
312
313 TEST_BARRIER();
314
315 /* should fail */
316 TEST_FAULT(t);
317 memcmp(a1, a2, STACK_ARRAY_SZ+1);
318 TEST_NOFAULT(t);
319
320 return 0;
321 }
322
323 static int test_bcmp(struct kasan_test *t)
324 {
325 TEST_START(t);
326 uint8_t *a1;
327 uint8_t *a2;
328
329 a1 = kalloc(STACK_ARRAY_SZ);
330 if (!a1)
331 return 1;
332 a2 = kalloc(STACK_ARRAY_SZ+1);
333 if (!a2)
334 return 1;
335
336 /* should work */
337 bcmp(a1, a2, STACK_ARRAY_SZ);
338 bcmp(a1, a2+1, STACK_ARRAY_SZ);
339
340 TEST_BARRIER();
341
342 /* should fail */
343 TEST_FAULT(t);
344 bcmp(a1, a2, STACK_ARRAY_SZ+1);
345 TEST_NOFAULT(t);
346
347 return 0;
348 }
349
350 static int test_bzero(struct kasan_test *t)
351 {
352 TEST_START(t);
353 uint8_t a1[STACK_ARRAY_SZ];
354
355 /* should work */
356 bzero(a1, STACK_ARRAY_SZ);
357
358 TEST_BARRIER();
359
360 /* should fail */
361 TEST_FAULT(t);
362 bzero(a1, STACK_ARRAY_SZ+1);
363 TEST_NOFAULT(t);
364
365 return 0;
366 }
367
368 static int test_strlcpy(struct kasan_test *t)
369 {
370 TEST_START(t);
371 char a1[8];
372
373 /* should not fault */
374 strlcpy(a1, "small", 8);
375 strlcpy(a1, "looooonnnnggg", 8);
376
377 TEST_FAULT(t);
378 strlcpy(a1, "looooooooonnnnggg", 9);
379 TEST_NOFAULT(t);
380
381 return 0;
382 }
383
384 static int test_strncpy(struct kasan_test *t)
385 {
386 TEST_START(t);
387 char a1[9];
388
389 /* should not fault */
390 strncpy(a1, "small", 9);
391 strncpy(a1, "looooonnnnggg", 9);
392
393 TEST_FAULT(t);
394 strncpy(a1, "looooonnnnggg", 10);
395 TEST_NOFAULT(t);
396
397 return a1[0] != 'l';
398 }
399
400 static int test_strlcat(struct kasan_test *t)
401 {
402 TEST_START(t);
403 char a1[9] = {};
404
405 /* should not fault */
406 strlcat(a1, "abcd", 9);
407 strlcat(a1, "efgh", 9);
408 strlcat(a1, "ijkl", 9);
409 a1[0] = '\0';
410 strlcat(a1, "looooonnnnggg", 9);
411
412 a1[0] = '\0';
413 TEST_FAULT(t);
414 strlcat(a1, "looooonnnnggg", 10);
415 TEST_NOFAULT(t);
416
417 return a1[0] != 'l';
418 }
419
420 static int test_strncat(struct kasan_test *t)
421 {
422 TEST_START(t);
423 char a1[9] = {};
424
425 /* should not fault */
426 strncat(a1, "abcd", 4);
427 strncat(a1, "efgh", 4);
428
429 TEST_FAULT(t);
430 strncat(a1, "i", 1);
431 TEST_NOFAULT(t);
432
433 return a1[0] != 'a';
434 }
435
436 /* we ignore the top *two* frames in backtrace - so add an extra one */
437 static int __attribute__((noinline))
438 test_blacklist_helper(void)
439 {
440 return kasan_is_blacklisted(TYPE_TEST);
441 }
442
443 static int __attribute__((noinline))
444 test_blacklist(struct kasan_test *t)
445 {
446 TEST_START(t);
447 int res = (int)!test_blacklist_helper();
448 TEST_DONE(t, res);
449 return 0;
450 }
451
452 static int __attribute__((noinline))
453 test_blacklist_str(struct kasan_test *t)
454 {
455 TEST_START(t);
456 char a1[8];
457
458 bcopy("123456", a1, 8);
459
460 TEST_DONE(t, 0); /* success */
461 return 0;
462 }
463
464 #if 0
465 static int test_strnlen(struct kasan_test *t)
466 {
467 TEST_START(t);
468 const char *a1 = "abcdef";
469
470 /* should not fault */
471 if (strnlen(a1, 6) != 6)
472 return 1;
473 if (strnlen(a1, 7) != 6)
474 return 1;
475
476 TEST_FAULT(t);
477 if (strnlen(a1, 8) != 6)
478 return 1;
479 TEST_NOFAULT(t);
480
481 return a1[0] != 'a';
482 }
483 #endif
484
485 int *uaf_ptr;
486 static int * NOINLINE
487 stack_uaf_helper(void)
488 {
489 int x;
490 uaf_ptr = &x;
491 return uaf_ptr;
492 }
493
494 static int test_stack_uaf(struct kasan_test __unused *t)
495 {
496 int *x = stack_uaf_helper();
497 *x = 0xb4d;
498 TEST_BARRIER();
499 return !(*x == 0xb4d);
500 }
501
502 static struct kasan_test xnu_tests[] = {
503 DECLARE_TEST(NULL, NULL),
504 DECLARE_TEST(test_global_overflow, "Global overflow"),
505 DECLARE_TEST3(test_heap_underflow, heap_cleanup, "Heap underflow"),
506 DECLARE_TEST3(test_heap_overflow, heap_cleanup, "Heap overflow"),
507 DECLARE_TEST(test_heap_uaf, "Heap use-after-free"),
508 DECLARE_TEST(test_heap_inval_free, "Heap invalid free"),
509 DECLARE_TEST(test_heap_double_free,"Heap double free"),
510 DECLARE_TEST3(test_heap_small_free, heap_cleanup, "Heap small free"),
511 DECLARE_TEST(test_stack_overflow, "Stack overflow"),
512 DECLARE_TEST(test_stack_underflow, "Stack underflow"),
513 DECLARE_TEST(test_stack_uaf, "Stack use-after-return"),
514 DECLARE_TEST(test_memcpy, "memcpy"),
515 DECLARE_TEST(test_memmove, "memmmove"),
516 DECLARE_TEST(test_bcopy, "bcopy"),
517 DECLARE_TEST(test_memset, "memset"),
518 DECLARE_TEST(test_memcmp, "memcmp"),
519 DECLARE_TEST(test_bcmp, "bcmp"),
520 DECLARE_TEST(test_bzero, "bzero"),
521 DECLARE_TEST(test_strlcpy, "strlcpy"),
522 DECLARE_TEST(test_strlcat, "strlcat"),
523 DECLARE_TEST(test_strncpy, "strncpy"),
524 DECLARE_TEST(test_strncat, "strncat"),
525 DECLARE_TEST(test_blacklist, "blacklist"),
526 DECLARE_TEST(test_blacklist_str, "blacklist_str"),
527 // DECLARE_TEST(test_strnlen, "strnlen"),
528 };
529 static int num_xnutests = sizeof(xnu_tests)/sizeof(xnu_tests[0]);
530
531 static int
532 kasan_run_test(struct kasan_test *test_list, int testno, int fail)
533 {
534 int status = TEST_UNKNOWN;
535 struct kasan_test *t = &test_list[testno];
536
537 if (testno < 0 || testno >= num_xnutests || !t->func) {
538 printf("KASan: test.%02d INVALID\n", testno);
539 return TEST_INVALID;
540 }
541
542 // printf("KASan: test.%02d RUNNING (%s)\n", testno, t->name);
543
544 if (!fail) {
545 in_test = 1;
546 }
547
548 if (_setjmp(jbuf) == 0) {
549 t->result = 0;
550 int ret = t->func(t);
551 if (ret) {
552 printf("KASan: test.%02d SETUP FAIL (%s)\n", testno, t->name);
553 status = ret;
554 } else {
555 /* did not fault when it should have */
556 printf("KASan: test.%02d FAIL (%s)\n", testno, t->name);
557 status = TEST_FAIL_NOFAULT;
558 }
559 } else {
560 /* Triggering a KASan violation will return here by longjmp, bypassing
561 * stack unpoisoning, so do it here explicitly. We just hope that
562 * fakestack free will happen later... */
563 kasan_unpoison_curstack(true);
564
565 if (t->result) {
566 /* faulted, but at the wrong place */
567 printf("KASan: test.%02d FAIL %d (%s)\n", testno, t->result, t->name);
568 status = TEST_FAIL_BADFAULT;
569 } else {
570 printf("KASan: test.%02d PASS (%s)\n", testno, t->name);
571 status = TEST_PASS;
572 }
573 }
574 in_test = 0;
575 if (t->cleanup) {
576 t->cleanup(t);
577 }
578
579 return status;
580 }
581
582 void
583 kasan_test(int testno, int fail)
584 {
585 int i = 1;
586 int pass = 0, total = 0;
587 int ret;
588
589 if (testno == -1) {
590 /* shorthand for all tests */
591 testno = (1U << (num_xnutests-1)) - 1;
592 }
593
594 while (testno) {
595 if (testno & 0x1) {
596 ret = kasan_run_test(xnu_tests, i, fail);
597 if (ret == TEST_PASS) {
598 pass++;
599 }
600 if (ret != TEST_INVALID) {
601 total++;
602 }
603 }
604
605 i++;
606 testno >>= 1;
607 }
608 printf("KASan: TEST SUMMARY %d/%d passed\n", pass, total);
609 }
610
611 void
612 kasan_handle_test(void)
613 {
614 if (in_test) {
615 _longjmp(jbuf, 1);
616 /* NOTREACHED */
617 }
618 }
619
620 void
621 __kasan_runtests(struct kasan_test *kext_tests, int numtests)
622 {
623 int i;
624 for (i = 0; i < numtests; i++) {
625 kasan_run_test(kext_tests, i, 0);
626 }
627 }