2 #include <kern/backtrace.h>
3 #include <kern/kalloc.h>
4 #include <kern/assert.h>
5 #include <kern/debug.h>
6 #include <kern/zalloc.h>
7 #include <kern/simple_lock.h>
8 #include <kern/locks.h>
9 #include <machine/machine_routines.h>
10 #include <libkern/libkern.h>
11 #include <libkern/tree.h>
12 #include <libkern/kernel_mach_header.h>
13 #include <libkern/OSKextLib.h>
14 #include <mach-o/loader.h>
15 #include <mach-o/nlist.h>
18 #include "kasan_internal.h"
20 #if KASAN_DYNAMIC_BLACKLIST
23 #define HASH_NBUCKETS 128U
24 #define HASH_MASK (HASH_NBUCKETS-1)
25 #define HASH_CACHE_NENTRIES 128
27 struct blacklist_entry
{
28 const char *kext_name
;
29 const char *func_name
;
36 #include "kasan_blacklist_dynamic.h"
37 /* defines 'blacklist' and 'blacklist_entries' */
39 decl_simple_lock_data(static, _dybl_lock
);
40 static access_t blacklisted_types
; /* bitmap of access types with blacklist entries */
43 dybl_lock(boolean_t
*b
)
45 *b
= ml_set_interrupts_enabled(false);
46 simple_lock(&_dybl_lock
);
50 dybl_unlock(boolean_t b
)
52 simple_unlock(&_dybl_lock
);
53 ml_set_interrupts_enabled(b
);
58 * blacklist call site hash table
61 struct blacklist_hash_entry
{
62 SLIST_ENTRY(blacklist_hash_entry
) chain
; // next element in chain
63 struct blacklist_entry
*ble
; // blacklist entry that this caller is an instance of
64 uintptr_t addr
; // callsite address
65 uint64_t count
; // hit count
68 struct hash_chain_head
{
69 SLIST_HEAD(, blacklist_hash_entry
);
72 unsigned cache_next_entry
= 0;
73 struct blacklist_hash_entry blhe_cache
[HASH_CACHE_NENTRIES
];
74 struct hash_chain_head hash_buckets
[HASH_NBUCKETS
];
76 static struct blacklist_hash_entry
*
77 alloc_hash_entry(void)
79 unsigned idx
= cache_next_entry
++;
80 if (idx
>= HASH_CACHE_NENTRIES
) {
81 cache_next_entry
= HASH_CACHE_NENTRIES
; // avoid overflow
84 return &blhe_cache
[idx
];
88 hash_addr(uintptr_t addr
)
90 addr
^= (addr
>> 7); /* mix in some of the bits likely to select the kext */
91 return (unsigned)addr
& HASH_MASK
;
94 static struct blacklist_hash_entry
*
95 blacklist_hash_lookup(uintptr_t addr
)
97 unsigned idx
= hash_addr(addr
);
98 struct blacklist_hash_entry
*blhe
;
100 SLIST_FOREACH(blhe
, &hash_buckets
[idx
], chain
) {
101 if (blhe
->addr
== addr
) {
109 static struct blacklist_hash_entry
*
110 blacklist_hash_add(uintptr_t addr
, struct blacklist_entry
*ble
)
112 unsigned idx
= hash_addr(addr
);
114 struct blacklist_hash_entry
*blhe
= alloc_hash_entry();
123 SLIST_INSERT_HEAD(&hash_buckets
[idx
], blhe
, chain
);
131 if (cache_next_entry
> 0) {
132 bzero(&hash_buckets
, sizeof(hash_buckets
));
133 bzero(&blhe_cache
, sizeof(struct blacklist_hash_entry
) * cache_next_entry
);
134 cache_next_entry
= 0;
139 * kext range lookup tree
142 struct range_tree_entry
{
143 RB_ENTRY(range_tree_entry
) tree
;
149 uint64_t accessed
: 1; // blacklist entry exists in this range
153 const char *bundleid
;
155 /* mach header for corresponding kext */
156 kernel_mach_header_t
*mh
;
160 range_tree_cmp(const struct range_tree_entry
*e1
, const struct range_tree_entry
*e2
)
162 if (e1
->size
== 0 || e2
->size
== 0) {
164 if (e1
->base
+ e1
->size
< e2
->base
) {
166 } else if (e1
->base
> e2
->base
+ e2
->size
) {
173 if (e1
->base
+ e1
->size
<= e2
->base
) {
175 } else if (e1
->base
>= e2
->base
+ e2
->size
) {
178 panic("bad compare\n");
184 RB_HEAD(range_tree
, range_tree_entry
) range_tree_root
;
185 RB_PROTOTYPE(range_tree
, range_tree_entry
, tree
, range_tree_cmp
);
186 RB_GENERATE(range_tree
, range_tree_entry
, tree
, range_tree_cmp
);
188 /* for each executable section, insert a range tree entry */
190 kasan_dybl_load_kext(uintptr_t addr
, const char *kextname
)
194 struct load_command
*cmd
= NULL
;
195 kernel_mach_header_t
*mh
= (void *)addr
;
197 cmd
= (struct load_command
*)&mh
[1];
199 for (i
= 0; i
< (int)mh
->ncmds
; i
++) {
200 if (cmd
->cmd
== LC_SEGMENT_KERNEL
) {
201 kernel_segment_command_t
*seg
= (void *)cmd
;
202 bool is_exec
= seg
->initprot
& VM_PROT_EXECUTE
;
205 if (is_exec
&& strcmp("__TEXT_EXEC", seg
->segname
) != 0) {
211 struct range_tree_entry
*e
= kalloc(sizeof(struct range_tree_entry
));
212 bzero(e
, sizeof(*e
));
214 e
->base
= seg
->vmaddr
;
215 e
->size
= seg
->vmsize
;
216 e
->bundleid
= kextname
;
221 RB_INSERT(range_tree
, &range_tree_root
, e
);
226 cmd
= (void *)((uintptr_t)cmd
+ cmd
->cmdsize
);
231 kasan_dybl_unload_kext(uintptr_t addr
)
235 struct load_command
*cmd
= NULL
;
236 kernel_mach_header_t
*mh
= (void *)addr
;
238 cmd
= (struct load_command
*)&mh
[1];
240 for (i
= 0; i
< (int)mh
->ncmds
; i
++) {
241 if (cmd
->cmd
== LC_SEGMENT_KERNEL
) {
242 kernel_segment_command_t
*seg
= (void *)cmd
;
243 bool is_exec
= seg
->initprot
& VM_PROT_EXECUTE
;
246 if (is_exec
&& strcmp("__TEXT_EXEC", seg
->segname
) != 0) {
252 struct range_tree_entry key
= { .base
= seg
->vmaddr
, .size
= 0 };
253 struct range_tree_entry
*e
;
256 e
= RB_FIND(range_tree
, &range_tree_root
, &key
);
258 RB_REMOVE(range_tree
, &range_tree_root
, e
);
260 /* there was a blacklist entry in this range */
267 kfree(e
, sizeof(*e
));
272 cmd
= (void *)((uintptr_t)cmd
+ cmd
->cmdsize
);
277 * return the closest function name at or before addr
279 static const NOINLINE
char *
280 addr_to_func(uintptr_t addr
, const kernel_mach_header_t
*mh
)
283 uintptr_t cur_addr
= 0;
285 const struct load_command
*cmd
= NULL
;
286 const struct symtab_command
*st
= NULL
;
287 const kernel_segment_command_t
*le
= NULL
;
289 const kernel_nlist_t
*syms
;
290 const char *cur_name
= NULL
;
292 cmd
= (const struct load_command
*)&mh
[1];
295 * find the symtab command and linkedit segment
297 for (i
= 0; i
< (int)mh
->ncmds
; i
++) {
298 if (cmd
->cmd
== LC_SYMTAB
) {
299 st
= (const struct symtab_command
*)cmd
;
300 } else if (cmd
->cmd
== LC_SEGMENT_KERNEL
) {
301 const kernel_segment_command_t
*seg
= (const void *)cmd
;
302 if (!strcmp(seg
->segname
, SEG_LINKEDIT
)) {
303 le
= (const void *)cmd
;
306 cmd
= (const void *)((uintptr_t)cmd
+ cmd
->cmdsize
);
309 /* locate the symbols and strings in the symtab */
310 strings
= (const void *)((le
->vmaddr
- le
->fileoff
) + st
->stroff
);
311 syms
= (const void *)((le
->vmaddr
- le
->fileoff
) + st
->symoff
);
314 * iterate the symbols, looking for the closest one to `addr'
316 for (i
= 0; i
< (int)st
->nsyms
; i
++) {
318 uint8_t n_type
= syms
[i
].n_type
;
319 const char *name
= strings
+ syms
[i
].n_un
.n_strx
;
321 if (n_type
& N_STAB
) {
322 /* ignore debug entries */
327 if (syms
[i
].n_un
.n_strx
== 0 || !(n_type
== N_SECT
|| n_type
== N_ABS
)) {
328 /* only use named and defined symbols */
333 if (mh
!= &_mh_execute_header
) {
334 printf("sym '%s' 0x%x 0x%lx\n", name
, (unsigned)syms
[i
].n_type
, (unsigned long)syms
[i
].n_value
);
342 /* this symbol is closer than the one we had */
343 if (syms
[i
].n_value
<= addr
&& syms
[i
].n_value
> cur_addr
) {
345 cur_addr
= syms
[i
].n_value
;
349 /* best guess for name of function at addr */
354 kasan_is_blacklisted(access_t type
)
356 uint32_t nframes
= 0;
357 uintptr_t frames
[MAX_FRAMES
];
358 uintptr_t *bt
= frames
;
360 assert(__builtin_popcount(type
) == 1);
362 if ((type
& blacklisted_types
) == 0) {
363 /* early exit for types with no blacklist entries */
367 nframes
= backtrace_frame(bt
, MAX_FRAMES
, __builtin_frame_address(0));
371 /* ignore direct caller */
376 struct blacklist_hash_entry
*blhe
= NULL
;
380 /* First check if any frame hits in the hash */
381 for (uint32_t i
= 0; i
< nframes
; i
++) {
382 blhe
= blacklist_hash_lookup(bt
[i
]);
384 if ((blhe
->ble
->type_mask
& type
) != type
) {
392 // printf("KASan: blacklist cache hit (%s:%s [0x%lx] 0x%x)\n",
393 // ble->kext_name ?: "" , ble->func_name ?: "", VM_KERNEL_UNSLIDE(bt[i]), mask);
399 /* no hits - slowpath */
400 for (uint32_t i
= 0; i
< nframes
; i
++) {
402 const char *kextname
= NULL
;
403 const char *funcname
= NULL
;
405 struct range_tree_entry key
= { .base
= bt
[i
], .size
= 0 };
406 struct range_tree_entry
*e
= RB_FIND(range_tree
, &range_tree_root
, &key
);
409 /* no match at this address - kinda weird? */
413 /* get the function and bundle name for the current frame */
414 funcname
= addr_to_func(bt
[i
], e
->mh
);
416 kextname
= strrchr(e
->bundleid
, '.');
420 kextname
= e
->bundleid
;
424 // printf("%s: a = 0x%016lx,0x%016lx f = %s, k = %s\n", __func__, bt[i], VM_KERNEL_UNSLIDE(bt[i]), funcname, kextname);
426 /* check if kextname or funcname are in the blacklist */
427 for (size_t j
= 0; j
< blacklist_entries
; j
++) {
428 struct blacklist_entry
*ble
= &blacklist
[j
];
431 if ((ble
->type_mask
& type
) != type
) {
436 if (ble
->kext_name
&& kextname
&& strncmp(kextname
, ble
->kext_name
, KMOD_MAX_NAME
) != 0) {
437 /* wrong kext name */
441 if (ble
->func_name
&& funcname
&& strncmp(funcname
, ble
->func_name
, 128) != 0) {
442 /* wrong func name */
446 /* found a matching function or kext */
447 blhe
= blacklist_hash_add(bt
[i
], ble
);
448 count
= ble
->count
++;
454 printf("KASan: ignoring blacklisted violation (%s:%s [0x%lx] %d 0x%x)\n",
455 kextname
, funcname
, VM_KERNEL_UNSLIDE(bt
[i
]), i
, type
);
467 add_blacklist_entry(const char *kext
, const char *func
, access_t type
)
469 assert(kext
|| func
);
470 struct blacklist_entry
*ble
= &blacklist
[blacklist_entries
++];
472 if (blacklist_entries
> blacklist_max_entries
) {
473 panic("KASan: dynamic blacklist entries exhausted\n");
477 size_t sz
= __nosan_strlen(kext
) + 1;
479 char *s
= kalloc(sz
);
480 __nosan_strlcpy(s
, kext
, sz
);
486 size_t sz
= __nosan_strlen(func
) + 1;
488 char *s
= kalloc(sz
);
489 __nosan_strlcpy(s
, func
, sz
);
494 ble
->type_mask
= type
;
497 #define TS(x) { .type = TYPE_##x, .str = #x }
499 static const struct {
501 const char * const str
;
526 /* convenience aliases */
527 { .type
= TYPE_POISON_GLOBAL
, .str
= "GLOB" },
528 { .type
= TYPE_POISON_HEAP
, .str
= "HEAP" },
530 static size_t typemap_sz
= sizeof(typemap
)/sizeof(typemap
[0]);
532 static inline access_t
533 map_type(const char *str
)
535 if (strlen(str
) == 0) {
539 /* convert type string to integer ID */
540 for (size_t i
= 0; i
< typemap_sz
; i
++) {
541 if (strcasecmp(str
, typemap
[i
].str
) == 0) {
542 return typemap
[i
].type
;
546 printf("KASan: unknown blacklist type `%s', assuming `normal'\n", str
);
551 kasan_init_dybl(void)
553 simple_lock_init(&_dybl_lock
, 0);
556 * dynamic blacklist entries via boot-arg. Syntax is:
557 * kasan.bl=kext1:func1:type1,kext2:func2:type2,...
561 if (PE_parse_boot_arg_str("kasan.bl", bufp
, sizeof(buf
))) {
563 while ((kext
= strsep(&bufp
, ",")) != NULL
) {
564 access_t type
= TYPE_NORMAL
;
565 char *func
= strchr(kext
, ':');
569 char *typestr
= strchr(func
, ':');
572 type
= map_type(typestr
);
574 add_blacklist_entry(kext
, func
, type
);
578 /* collect bitmask of blacklisted types */
579 for (size_t j
= 0; j
< blacklist_entries
; j
++) {
580 struct blacklist_entry
*ble
= &blacklist
[j
];
581 blacklisted_types
|= ble
->type_mask
;
584 /* add the fake kernel kext */
585 kasan_dybl_load_kext((uintptr_t)&_mh_execute_header
, "__kernel__");
588 #else /* KASAN_DYNAMIC_BLACKLIST */
591 kasan_is_blacklisted(access_t __unused type
)