2 * Copyright (c) 2016 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
31 #include <kern/assert.h>
32 #include <kern/zalloc.h>
33 #include <mach/mach_vm.h>
34 #include <mach/vm_param.h>
35 #include <libkern/libkern.h>
36 #include <libkern/OSAtomic.h>
37 #include <sys/queue.h>
38 #include <kern/thread.h>
39 #include <kern/debug.h>
42 #include <kasan_internal.h>
44 int __asan_option_detect_stack_use_after_return
= 0;
45 int fakestack_enabled
= 0;
47 #define FAKESTACK_HEADER_SZ 64
48 #define FAKESTACK_NUM_SZCLASS 7
50 #define FAKESTACK_FREED 0 /* forced by clang */
51 #define FAKESTACK_ALLOCATED 1
55 struct fakestack_header
{
56 LIST_ENTRY(fakestack_header
) list
;
57 void *site
; /* allocation site */
60 vm_size_t realsz
: 52;
61 vm_size_t sz_class
: 4;
65 _Static_assert(sizeof(struct fakestack_header
) <= FAKESTACK_HEADER_SZ
, "fakestack_header size mismatch");
67 static zone_t fakestack_zones
[FAKESTACK_NUM_SZCLASS
];
68 static char fakestack_names
[FAKESTACK_NUM_SZCLASS
][16];
69 static const unsigned long fakestack_min
= 1 << 6;
70 static const unsigned long __unused fakestack_max
= 1 << 16;
73 * Enter a fakestack critical section in a reentrant-safe fashion. Returns true on
74 * success with the kasan lock held.
77 thread_enter_fakestack(boolean_t
*flags
)
79 thread_t cur
= current_thread();
80 if (cur
&& kasan_lock_held(cur
)) {
81 /* current thread is already in kasan - fail */
88 static volatile long suspend_count
;
89 static const long suspend_threshold
= 20;
92 kasan_fakestack_suspend(void)
94 if (OSIncrementAtomicLong(&suspend_count
) == suspend_threshold
) {
95 __asan_option_detect_stack_use_after_return
= 0;
100 kasan_fakestack_resume(void)
102 long orig
= OSDecrementAtomicLong(&suspend_count
);
105 if (fakestack_enabled
&& orig
== suspend_threshold
) {
106 __asan_option_detect_stack_use_after_return
= 1;
111 ptr_is_on_stack(uptr ptr
)
113 vm_offset_t base
= dtrace_get_kernel_stack(current_thread());
115 if (ptr
>= base
&& ptr
< (base
+ kernel_stack_size
)) {
122 /* free all unused fakestack entries */
124 kasan_fakestack_gc(thread_t thread
)
126 struct fakestack_header
*cur
, *tmp
;
127 LIST_HEAD(, fakestack_header
) tofree
= LIST_HEAD_INITIALIZER(tofree
);
129 /* move all the freed elements off the main list */
130 struct fakestack_header_list
*head
= &kasan_get_thread_data(thread
)->fakestack_head
;
131 LIST_FOREACH_SAFE(cur
, head
, list
, tmp
) {
132 if (cur
->flag
== FAKESTACK_FREED
) {
133 LIST_REMOVE(cur
, list
);
134 LIST_INSERT_HEAD(&tofree
, cur
, list
);
138 /* ... then actually free them */
139 LIST_FOREACH_SAFE(cur
, &tofree
, list
, tmp
) {
140 zone_t zone
= fakestack_zones
[cur
->sz_class
];
141 size_t sz
= (fakestack_min
<< cur
->sz_class
) + FAKESTACK_HEADER_SZ
;
142 LIST_REMOVE(cur
, list
);
144 void *ptr
= (void *)cur
;
145 kasan_free_internal(&ptr
, &sz
, KASAN_HEAP_FAKESTACK
, &zone
, cur
->realsz
, 1, FAKESTACK_QUARANTINE
);
153 fakestack_flag_ptr(vm_offset_t ptr
, vm_size_t sz
)
155 uint8_t **x
= (uint8_t **)ptr
;
160 static uptr ALWAYS_INLINE
161 kasan_fakestack_alloc(int sz_class
, size_t realsz
)
163 if (!__asan_option_detect_stack_use_after_return
) {
167 if (sz_class
>= FAKESTACK_NUM_SZCLASS
) {
172 size_t sz
= fakestack_min
<< sz_class
;
173 assert(realsz
<= sz
);
174 assert(sz
<= fakestack_max
);
175 zone_t zone
= fakestack_zones
[sz_class
];
178 if (!thread_enter_fakestack(&flags
)) {
182 kasan_fakestack_gc(current_thread()); /* XXX: optimal? */
184 ret
= (uptr
)zget(zone
);
187 size_t leftrz
= 32 + FAKESTACK_HEADER_SZ
;
188 size_t validsz
= realsz
- 32 - 16; /* remove redzones */
189 size_t rightrz
= sz
- validsz
- 32; /* 16 bytes, plus whatever is left over */
190 struct fakestack_header
*hdr
= (struct fakestack_header
*)ret
;
192 kasan_poison(ret
, validsz
, leftrz
, rightrz
, ASAN_STACK_RZ
);
194 hdr
->site
= __builtin_return_address(0);
195 hdr
->realsz
= realsz
;
196 hdr
->sz_class
= sz_class
;
197 hdr
->flag
= FAKESTACK_ALLOCATED
;
198 ret
+= FAKESTACK_HEADER_SZ
;
200 *fakestack_flag_ptr(ret
, sz
) = &hdr
->flag
; /* back ptr to the slot */
201 struct fakestack_header_list
*head
= &kasan_get_thread_data(current_thread())->fakestack_head
;
202 LIST_INSERT_HEAD(head
, hdr
, list
);
210 kasan_fakestack_free(int sz_class
, uptr dst
, size_t realsz
)
212 if (ptr_is_on_stack(dst
)) {
216 assert(realsz
<= (fakestack_min
<< sz_class
));
218 vm_size_t sz
= fakestack_min
<< sz_class
;
219 zone_t zone
= fakestack_zones
[sz_class
];
222 /* TODO: check the magic? */
224 dst
-= FAKESTACK_HEADER_SZ
;
225 sz
+= FAKESTACK_HEADER_SZ
;
227 struct fakestack_header
*hdr
= (struct fakestack_header
*)dst
;
228 assert(hdr
->sz_class
== sz_class
);
233 LIST_REMOVE(hdr
, list
);
235 kasan_free_internal((void **)&dst
, &sz
, KASAN_HEAP_FAKESTACK
, &zone
, realsz
, 1, FAKESTACK_QUARANTINE
);
237 zfree(zone
, (void *)dst
);
244 kasan_unpoison_fakestack(thread_t thread
)
247 if (!thread_enter_fakestack(&flags
)) {
248 panic("expected success entering fakestack\n");
251 struct fakestack_header_list
*head
= &kasan_get_thread_data(thread
)->fakestack_head
;
252 struct fakestack_header
*cur
;
253 LIST_FOREACH(cur
, head
, list
) {
254 if (cur
->flag
== FAKESTACK_ALLOCATED
) {
255 cur
->flag
= FAKESTACK_FREED
;
259 kasan_fakestack_gc(thread
);
264 kasan_init_fakestack(void)
266 /* allocate the fakestack zones */
267 for (int i
= 0; i
< FAKESTACK_NUM_SZCLASS
; i
++) {
269 unsigned long sz
= (fakestack_min
<< i
) + FAKESTACK_HEADER_SZ
;
270 size_t maxsz
= 256UL * 1024;
273 /* size classes 0..3 are much more common */
277 snprintf(fakestack_names
[i
], 16, "fakestack.%d", i
);
278 z
= zinit(sz
, maxsz
, sz
, fakestack_names
[i
]);
280 zone_change(z
, Z_NOCALLOUT
, TRUE
);
281 zone_change(z
, Z_EXHAUST
, TRUE
);
282 zone_change(z
, Z_EXPAND
, FALSE
);
283 zone_change(z
, Z_COLLECT
, FALSE
);
284 zone_change(z
, Z_KASAN_QUARANTINE
, FALSE
);
285 zfill(z
, maxsz
/ sz
);
286 fakestack_zones
[i
] = z
;
289 /* globally enable */
290 if (fakestack_enabled
) {
291 __asan_option_detect_stack_use_after_return
= 1;
295 #else /* FAKESTACK */
298 kasan_init_fakestack(void)
300 assert(__asan_option_detect_stack_use_after_return
== 0);
304 kasan_unpoison_fakestack(thread_t __unused thread
)
306 assert(__asan_option_detect_stack_use_after_return
== 0);
310 kasan_fakestack_alloc(int __unused sz_class
, size_t __unused realsz
)
312 assert(__asan_option_detect_stack_use_after_return
== 0);
317 kasan_fakestack_free(int __unused sz_class
, uptr __unused dst
, size_t __unused realsz
)
319 assert(__asan_option_detect_stack_use_after_return
== 0);
320 panic("fakestack_free called on non-FAKESTACK config\n");
325 void kasan_init_thread(struct kasan_thread_data
*td
)
327 LIST_INIT(&td
->fakestack_head
);
330 #define FAKESTACK_DECLARE(szclass) \
331 uptr __asan_stack_malloc_##szclass(size_t sz) { return kasan_fakestack_alloc(szclass, sz); } \
332 void __asan_stack_free_##szclass(uptr dst, size_t sz) { kasan_fakestack_free(szclass, dst, sz); }
344 FAKESTACK_DECLARE(10)