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
, LCK_GRP_NULL
);
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
;
204 #if defined(__arm__) || defined(__arm64__)
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
;
244 #if defined(__arm__) || defined(__arm64__)
245 if (is_exec
&& strcmp("__TEXT_EXEC", seg
->segname
) != 0) {
251 struct range_tree_entry key
= { .base
= seg
->vmaddr
, .size
= 0 };
252 struct range_tree_entry
*e
;
255 e
= RB_FIND(range_tree
, &range_tree_root
, &key
);
257 RB_REMOVE(range_tree
, &range_tree_root
, e
);
259 /* there was a blacklist entry in this range */
266 kfree(e
, sizeof(*e
));
271 cmd
= (void *)((uintptr_t)cmd
+ cmd
->cmdsize
);
276 * return the closest function name at or before addr
278 static const NOINLINE
char *
279 addr_to_func(uintptr_t addr
, const kernel_mach_header_t
*mh
)
282 uintptr_t cur_addr
= 0;
284 const struct load_command
*cmd
= NULL
;
285 const struct symtab_command
*st
= NULL
;
286 const kernel_segment_command_t
*le
= NULL
;
288 const kernel_nlist_t
*syms
;
289 const char *cur_name
= NULL
;
291 cmd
= (const struct load_command
*)&mh
[1];
294 * find the symtab command and linkedit segment
296 for (i
= 0; i
< (int)mh
->ncmds
; i
++) {
297 if (cmd
->cmd
== LC_SYMTAB
) {
298 st
= (const struct symtab_command
*)cmd
;
299 } else if (cmd
->cmd
== LC_SEGMENT_KERNEL
) {
300 const kernel_segment_command_t
*seg
= (const void *)cmd
;
301 if (!strcmp(seg
->segname
, SEG_LINKEDIT
)) {
302 le
= (const void *)cmd
;
305 cmd
= (const void *)((uintptr_t)cmd
+ cmd
->cmdsize
);
308 /* locate the symbols and strings in the symtab */
309 strings
= (const void *)((le
->vmaddr
- le
->fileoff
) + st
->stroff
);
310 syms
= (const void *)((le
->vmaddr
- le
->fileoff
) + st
->symoff
);
313 * iterate the symbols, looking for the closest one to `addr'
315 for (i
= 0; i
< (int)st
->nsyms
; i
++) {
316 uint8_t n_type
= syms
[i
].n_type
;
317 const char *name
= strings
+ syms
[i
].n_un
.n_strx
;
319 if (n_type
& N_STAB
) {
320 /* ignore debug entries */
325 if (syms
[i
].n_un
.n_strx
== 0 || !(n_type
== N_SECT
|| n_type
== N_ABS
)) {
326 /* only use named and defined symbols */
331 if (mh
!= &_mh_execute_header
) {
332 printf("sym '%s' 0x%x 0x%lx\n", name
, (unsigned)syms
[i
].n_type
, (unsigned long)syms
[i
].n_value
);
340 /* this symbol is closer than the one we had */
341 if (syms
[i
].n_value
<= addr
&& syms
[i
].n_value
> cur_addr
) {
343 cur_addr
= syms
[i
].n_value
;
347 /* best guess for name of function at addr */
352 kasan_is_blacklisted(access_t type
)
354 uint32_t nframes
= 0;
355 uintptr_t frames
[MAX_FRAMES
];
356 uintptr_t *bt
= frames
;
358 assert(__builtin_popcount(type
) == 1);
360 if ((type
& blacklisted_types
) == 0) {
361 /* early exit for types with no blacklist entries */
365 nframes
= backtrace_frame(bt
, MAX_FRAMES
, __builtin_frame_address(0),
370 /* ignore direct caller */
375 struct blacklist_hash_entry
*blhe
= NULL
;
379 /* First check if any frame hits in the hash */
380 for (uint32_t i
= 0; i
< nframes
; i
++) {
381 blhe
= blacklist_hash_lookup(bt
[i
]);
383 if ((blhe
->ble
->type_mask
& type
) != type
) {
391 // printf("KASan: blacklist cache hit (%s:%s [0x%lx] 0x%x)\n",
392 // ble->kext_name ?: "" , ble->func_name ?: "", VM_KERNEL_UNSLIDE(bt[i]), mask);
398 /* no hits - slowpath */
399 for (uint32_t i
= 0; i
< nframes
; i
++) {
400 const char *kextname
= NULL
;
401 const char *funcname
= NULL
;
403 struct range_tree_entry key
= { .base
= bt
[i
], .size
= 0 };
404 struct range_tree_entry
*e
= RB_FIND(range_tree
, &range_tree_root
, &key
);
407 /* no match at this address - kinda weird? */
411 /* get the function and bundle name for the current frame */
412 funcname
= addr_to_func(bt
[i
], e
->mh
);
414 kextname
= strrchr(e
->bundleid
, '.');
418 kextname
= e
->bundleid
;
422 // printf("%s: a = 0x%016lx,0x%016lx f = %s, k = %s\n", __func__, bt[i], VM_KERNEL_UNSLIDE(bt[i]), funcname, kextname);
424 /* check if kextname or funcname are in the blacklist */
425 for (size_t j
= 0; j
< blacklist_entries
; j
++) {
426 struct blacklist_entry
*ble
= &blacklist
[j
];
429 if ((ble
->type_mask
& type
) != type
) {
434 if (ble
->kext_name
&& kextname
&& strncmp(kextname
, ble
->kext_name
, KMOD_MAX_NAME
) != 0) {
435 /* wrong kext name */
439 if (ble
->func_name
&& funcname
&& strncmp(funcname
, ble
->func_name
, 128) != 0) {
440 /* wrong func name */
444 /* found a matching function or kext */
445 blhe
= blacklist_hash_add(bt
[i
], ble
);
446 count
= ble
->count
++;
452 printf("KASan: ignoring blacklisted violation (%s:%s [0x%lx] %d 0x%x)\n",
453 kextname
, funcname
, VM_KERNEL_UNSLIDE(bt
[i
]), i
, type
);
465 add_blacklist_entry(const char *kext
, const char *func
, access_t type
)
467 assert(kext
|| func
);
468 struct blacklist_entry
*ble
= &blacklist
[blacklist_entries
++];
470 if (blacklist_entries
> blacklist_max_entries
) {
471 panic("KASan: dynamic blacklist entries exhausted\n");
475 size_t sz
= __nosan_strlen(kext
) + 1;
477 char *s
= zalloc_permanent(sz
, ZALIGN_NONE
);
478 __nosan_strlcpy(s
, kext
, sz
);
484 size_t sz
= __nosan_strlen(func
) + 1;
486 char *s
= zalloc_permanent(sz
, ZALIGN_NONE
);
487 __nosan_strlcpy(s
, func
, sz
);
492 ble
->type_mask
= type
;
495 #define TS(x) { .type = TYPE_##x, .str = #x }
497 static const struct {
499 const char * const str
;
524 /* convenience aliases */
525 { .type
= TYPE_POISON_GLOBAL
, .str
= "GLOB" },
526 { .type
= TYPE_POISON_HEAP
, .str
= "HEAP" },
528 static size_t typemap_sz
= sizeof(typemap
) / sizeof(typemap
[0]);
530 static inline access_t
531 map_type(const char *str
)
533 if (strlen(str
) == 0) {
537 /* convert type string to integer ID */
538 for (size_t i
= 0; i
< typemap_sz
; i
++) {
539 if (strcasecmp(str
, typemap
[i
].str
) == 0) {
540 return typemap
[i
].type
;
544 printf("KASan: unknown blacklist type `%s', assuming `normal'\n", str
);
549 kasan_init_dybl(void)
551 simple_lock_init(&_dybl_lock
, 0);
554 * dynamic blacklist entries via boot-arg. Syntax is:
555 * kasan.bl=kext1:func1:type1,kext2:func2:type2,...
559 if (PE_parse_boot_arg_str("kasan.bl", bufp
, sizeof(buf
))) {
561 while ((kext
= strsep(&bufp
, ",")) != NULL
) {
562 access_t type
= TYPE_NORMAL
;
563 char *func
= strchr(kext
, ':');
567 char *typestr
= strchr(func
, ':');
570 type
= map_type(typestr
);
572 add_blacklist_entry(kext
, func
, type
);
576 /* collect bitmask of blacklisted types */
577 for (size_t j
= 0; j
< blacklist_entries
; j
++) {
578 struct blacklist_entry
*ble
= &blacklist
[j
];
579 blacklisted_types
|= ble
->type_mask
;
582 /* add the fake kernel kext */
583 kasan_dybl_load_kext((uintptr_t)&_mh_execute_header
, "__kernel__");
586 #else /* KASAN_DYNAMIC_BLACKLIST */
589 kasan_is_blacklisted(access_t __unused type
)