]>
Commit | Line | Data |
---|---|---|
5ba3f43e A |
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> | |
d9a64523 | 37 | #include <kern/thread.h> |
5ba3f43e A |
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) | |
0a7de745 A |
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 } | |
5ba3f43e | 82 | |
0a7de745 A |
83 | static void |
84 | heap_cleanup(struct kasan_test *t) | |
5ba3f43e A |
85 | { |
86 | if (t->data) { | |
87 | kfree(t->data, t->datasz); | |
88 | t->data = NULL; | |
89 | } | |
90 | } | |
91 | ||
0a7de745 A |
92 | static int |
93 | test_global_overflow(struct kasan_test __unused *t) | |
5ba3f43e A |
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 | ||
0a7de745 A |
103 | static int |
104 | test_heap_underflow(struct kasan_test __unused *t) | |
5ba3f43e A |
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 | ||
0a7de745 A |
116 | static int |
117 | test_heap_overflow(struct kasan_test __unused *t) | |
5ba3f43e A |
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 | ||
0a7de745 A |
129 | static int |
130 | test_heap_uaf(struct kasan_test __unused *t) | |
5ba3f43e A |
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 | ||
0a7de745 A |
141 | static int |
142 | test_heap_inval_free(struct kasan_test __unused *t) | |
5ba3f43e A |
143 | { |
144 | int x; | |
0a7de745 A |
145 | int *ptr = &x; |
146 | kfree(ptr, BUFSZ); | |
5ba3f43e A |
147 | return 0; |
148 | } | |
149 | ||
0a7de745 A |
150 | static int |
151 | test_heap_double_free(struct kasan_test *t) | |
5ba3f43e A |
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 | ||
0a7de745 A |
167 | static int |
168 | test_heap_small_free(struct kasan_test *t) | |
5ba3f43e A |
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); | |
0a7de745 | 180 | kfree(x, BUFSZ - 2); |
5ba3f43e A |
181 | t->data = NULL; |
182 | t->datasz = 0; | |
183 | ||
184 | return 0; | |
185 | } | |
186 | ||
0a7de745 A |
187 | static int |
188 | test_stack_overflow(struct kasan_test *t) | |
5ba3f43e A |
189 | { |
190 | TEST_START(t); | |
191 | ||
f427ee49 | 192 | uint8_t i; |
5ba3f43e A |
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 | ||
0a7de745 A |
208 | static int |
209 | test_stack_underflow(struct kasan_test *t) | |
5ba3f43e A |
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__ | |
0a7de745 | 220 | __asm__ __volatile__ ("movq $-1, %0" : "=r"(idx) :: "memory"); |
5ba3f43e | 221 | #else |
0a7de745 | 222 | __asm__ __volatile__ ("mov %0, #-1" : "=r"(idx) :: "memory"); |
5ba3f43e A |
223 | #endif |
224 | ||
225 | TEST_FAULT(t); | |
226 | a[idx] = 0xbd; | |
227 | TEST_NOFAULT(t); | |
228 | ||
229 | TEST_BARRIER(); | |
0a7de745 | 230 | return a[0] == 0; |
5ba3f43e A |
231 | } |
232 | ||
0a7de745 A |
233 | static int |
234 | test_memcpy(struct kasan_test *t) | |
5ba3f43e A |
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); | |
0a7de745 | 247 | memcpy(a2, a1, STACK_ARRAY_SZ + 1); |
5ba3f43e A |
248 | TEST_NOFAULT(t); |
249 | ||
250 | return 0; | |
251 | } | |
252 | ||
0a7de745 A |
253 | static int |
254 | test_memmove(struct kasan_test *t) | |
5ba3f43e A |
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); | |
0a7de745 | 267 | memmove(a2, a1, STACK_ARRAY_SZ + 1); |
5ba3f43e A |
268 | TEST_NOFAULT(t); |
269 | ||
270 | return 0; | |
271 | } | |
272 | ||
0a7de745 A |
273 | static int |
274 | test_bcopy(struct kasan_test *t) | |
5ba3f43e A |
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); | |
0a7de745 | 287 | bcopy(a2, a1, STACK_ARRAY_SZ + 1); |
5ba3f43e A |
288 | TEST_NOFAULT(t); |
289 | ||
290 | return 0; | |
291 | } | |
292 | ||
0a7de745 A |
293 | static int |
294 | test_memset(struct kasan_test *t) | |
5ba3f43e A |
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); | |
0a7de745 | 306 | memset(a1, 'f', STACK_ARRAY_SZ + 1); |
5ba3f43e A |
307 | TEST_NOFAULT(t); |
308 | ||
309 | return 0; | |
310 | } | |
311 | ||
0a7de745 A |
312 | static int |
313 | test_memcmp(struct kasan_test *t) | |
5ba3f43e A |
314 | { |
315 | TEST_START(t); | |
316 | uint8_t *a1; | |
317 | uint8_t *a2; | |
318 | ||
319 | a1 = kalloc(STACK_ARRAY_SZ); | |
0a7de745 | 320 | if (!a1) { |
5ba3f43e | 321 | return 1; |
0a7de745 A |
322 | } |
323 | a2 = kalloc(STACK_ARRAY_SZ + 1); | |
324 | if (!a2) { | |
5ba3f43e | 325 | return 1; |
0a7de745 | 326 | } |
5ba3f43e A |
327 | |
328 | /* should work */ | |
329 | memcmp(a1, a2, STACK_ARRAY_SZ); | |
0a7de745 | 330 | memcmp(a1, a2 + 1, STACK_ARRAY_SZ); |
5ba3f43e A |
331 | |
332 | TEST_BARRIER(); | |
333 | ||
334 | /* should fail */ | |
335 | TEST_FAULT(t); | |
0a7de745 | 336 | memcmp(a1, a2, STACK_ARRAY_SZ + 1); |
5ba3f43e A |
337 | TEST_NOFAULT(t); |
338 | ||
339 | return 0; | |
340 | } | |
341 | ||
0a7de745 A |
342 | static int |
343 | test_bcmp(struct kasan_test *t) | |
5ba3f43e A |
344 | { |
345 | TEST_START(t); | |
346 | uint8_t *a1; | |
347 | uint8_t *a2; | |
348 | ||
349 | a1 = kalloc(STACK_ARRAY_SZ); | |
0a7de745 | 350 | if (!a1) { |
5ba3f43e | 351 | return 1; |
0a7de745 A |
352 | } |
353 | a2 = kalloc(STACK_ARRAY_SZ + 1); | |
354 | if (!a2) { | |
5ba3f43e | 355 | return 1; |
0a7de745 | 356 | } |
5ba3f43e A |
357 | |
358 | /* should work */ | |
359 | bcmp(a1, a2, STACK_ARRAY_SZ); | |
0a7de745 | 360 | bcmp(a1, a2 + 1, STACK_ARRAY_SZ); |
5ba3f43e A |
361 | |
362 | TEST_BARRIER(); | |
363 | ||
364 | /* should fail */ | |
365 | TEST_FAULT(t); | |
0a7de745 | 366 | bcmp(a1, a2, STACK_ARRAY_SZ + 1); |
5ba3f43e A |
367 | TEST_NOFAULT(t); |
368 | ||
369 | return 0; | |
370 | } | |
371 | ||
0a7de745 A |
372 | static int |
373 | test_bzero(struct kasan_test *t) | |
5ba3f43e A |
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); | |
0a7de745 | 385 | bzero(a1, STACK_ARRAY_SZ + 1); |
5ba3f43e A |
386 | TEST_NOFAULT(t); |
387 | ||
388 | return 0; | |
389 | } | |
390 | ||
0a7de745 A |
391 | static int |
392 | test_strlcpy(struct kasan_test *t) | |
5ba3f43e A |
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 | ||
0a7de745 A |
408 | static int |
409 | test_strncpy(struct kasan_test *t) | |
5ba3f43e A |
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 | ||
0a7de745 A |
425 | static int |
426 | test_strlcat(struct kasan_test *t) | |
5ba3f43e A |
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 | ||
0a7de745 A |
446 | static int |
447 | test_strncat(struct kasan_test *t) | |
5ba3f43e A |
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 */ | |
d9a64523 | 464 | static int OS_NOINLINE |
a39ff7e2 | 465 | test_blacklist_helper(void) |
5ba3f43e A |
466 | { |
467 | return kasan_is_blacklisted(TYPE_TEST); | |
468 | } | |
469 | ||
d9a64523 | 470 | static int OS_NOINLINE |
a39ff7e2 | 471 | test_blacklist(struct kasan_test *t) |
5ba3f43e A |
472 | { |
473 | TEST_START(t); | |
474 | int res = (int)!test_blacklist_helper(); | |
475 | TEST_DONE(t, res); | |
476 | return 0; | |
477 | } | |
478 | ||
d9a64523 | 479 | static int OS_NOINLINE |
a39ff7e2 | 480 | test_blacklist_str(struct kasan_test *t) |
5ba3f43e A |
481 | { |
482 | TEST_START(t); | |
483 | char a1[8]; | |
484 | ||
a39ff7e2 | 485 | bcopy("123456", a1, 8); |
5ba3f43e A |
486 | |
487 | TEST_DONE(t, 0); /* success */ | |
488 | return 0; | |
489 | } | |
490 | ||
491 | #if 0 | |
0a7de745 A |
492 | static int |
493 | test_strnlen(struct kasan_test *t) | |
5ba3f43e A |
494 | { |
495 | TEST_START(t); | |
496 | const char *a1 = "abcdef"; | |
497 | ||
498 | /* should not fault */ | |
0a7de745 | 499 | if (strnlen(a1, 6) != 6) { |
5ba3f43e | 500 | return 1; |
0a7de745 A |
501 | } |
502 | if (strnlen(a1, 7) != 6) { | |
5ba3f43e | 503 | return 1; |
0a7de745 | 504 | } |
5ba3f43e A |
505 | |
506 | TEST_FAULT(t); | |
0a7de745 | 507 | if (strnlen(a1, 8) != 6) { |
5ba3f43e | 508 | return 1; |
0a7de745 | 509 | } |
5ba3f43e A |
510 | TEST_NOFAULT(t); |
511 | ||
512 | return a1[0] != 'a'; | |
513 | } | |
514 | #endif | |
515 | ||
d9a64523 A |
516 | static void OS_NOINLINE |
517 | force_fakestack(char *x) | |
518 | { | |
0a7de745 | 519 | __asm__ __volatile__ ("" :: "r" (x) : "memory"); |
d9a64523 A |
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 | ||
5ba3f43e A |
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 | ||
0a7de745 A |
569 | static int |
570 | test_stack_uaf(struct kasan_test __unused *t) | |
5ba3f43e A |
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"), | |
0a7de745 A |
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"), | |
5ba3f43e | 584 | DECLARE_TEST(test_heap_inval_free, "Heap invalid free"), |
0a7de745 | 585 | DECLARE_TEST(test_heap_double_free, "Heap double free"), |
5ba3f43e | 586 | DECLARE_TEST3(test_heap_small_free, heap_cleanup, "Heap small free"), |
0a7de745 | 587 | DECLARE_TEST(test_stack_overflow, "Stack overflow"), |
5ba3f43e | 588 | DECLARE_TEST(test_stack_underflow, "Stack underflow"), |
0a7de745 A |
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"), | |
5ba3f43e A |
604 | // DECLARE_TEST(test_strnlen, "strnlen"), |
605 | }; | |
0a7de745 | 606 | static int num_xnutests = sizeof(xnu_tests) / sizeof(xnu_tests[0]); |
5ba3f43e A |
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 { | |
5ba3f43e A |
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 */ | |
0a7de745 | 663 | testno = (1U << (num_xnutests - 1)) - 1; |
5ba3f43e A |
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 | } |