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;
46 #define FAKESTACK_HEADER_SZ 64
47 #define FAKESTACK_NUM_SZCLASS 7
49 #define FAKESTACK_FREED 0 /* forced by clang */
50 #define FAKESTACK_ALLOCATED 1
54 struct fakestack_header
{
55 LIST_ENTRY(fakestack_header
) list
;
56 void *site
; /* allocation site */
59 vm_size_t realsz
: 52;
60 vm_size_t sz_class
: 4;
64 _Static_assert(sizeof(struct fakestack_header
) <= FAKESTACK_HEADER_SZ
, "fakestack_header size mismatch");
66 static zone_t fakestack_zones
[FAKESTACK_NUM_SZCLASS
];
67 static char fakestack_names
[FAKESTACK_NUM_SZCLASS
][16];
68 static const unsigned long fakestack_min
= 1 << 6;
69 static const unsigned long __unused fakestack_max
= 1 << 16;
72 * Mark the current thread as being in a fakestack operation, to avoid reentrancy
73 * issues. If set, disable fakestack allocation.
76 thread_enter_fakestack(void)
78 thread_t thread
= current_thread();
80 return OSIncrementAtomic(&kasan_get_thread_data(current_thread())->in_fakestack
);
87 thread_exit_fakestack(void)
89 thread_t thread
= current_thread();
91 return OSDecrementAtomic(&kasan_get_thread_data(current_thread())->in_fakestack
);
98 ptr_is_on_stack(uptr ptr
)
100 vm_offset_t base
= dtrace_get_kernel_stack(current_thread());
102 if (ptr
>= base
&& ptr
< (base
+ kernel_stack_size
)) {
109 /* free all unused fakestack entries */
111 kasan_fakestack_gc(thread_t thread
)
113 struct fakestack_header
*cur
, *tmp
;
114 LIST_HEAD(, fakestack_header
) tofree
= LIST_HEAD_INITIALIZER(tofree
);
116 /* move all the freed elements off the main list */
117 struct fakestack_header_list
*head
= &kasan_get_thread_data(thread
)->fakestack_head
;
118 LIST_FOREACH_SAFE(cur
, head
, list
, tmp
) {
119 if (cur
->flag
== FAKESTACK_FREED
) {
120 LIST_REMOVE(cur
, list
);
121 LIST_INSERT_HEAD(&tofree
, cur
, list
);
125 /* ... then actually free them */
126 LIST_FOREACH_SAFE(cur
, &tofree
, list
, tmp
) {
127 zone_t zone
= fakestack_zones
[cur
->sz_class
];
128 size_t sz
= (fakestack_min
<< cur
->sz_class
) + FAKESTACK_HEADER_SZ
;
129 LIST_REMOVE(cur
, list
);
131 void *ptr
= (void *)cur
;
132 kasan_free_internal(&ptr
, &sz
, KASAN_HEAP_FAKESTACK
, &zone
, cur
->realsz
, 1, FAKESTACK_QUARANTINE
);
140 fakestack_flag_ptr(vm_offset_t ptr
, vm_size_t sz
)
142 uint8_t **x
= (uint8_t **)ptr
;
147 static uptr ALWAYS_INLINE
148 kasan_fakestack_alloc(int sz_class
, size_t realsz
)
150 if (!__asan_option_detect_stack_use_after_return
) {
154 if (sz_class
>= FAKESTACK_NUM_SZCLASS
) {
160 size_t sz
= fakestack_min
<< sz_class
;
161 assert(realsz
<= sz
);
162 assert(sz
<= fakestack_max
);
163 zone_t zone
= fakestack_zones
[sz_class
];
165 if (thread_enter_fakestack()) {
170 kasan_fakestack_gc(current_thread()); /* XXX: optimal? */
172 ret
= (uptr
)zget(zone
);
174 thread_exit_fakestack();
177 size_t leftrz
= 32 + FAKESTACK_HEADER_SZ
;
178 size_t validsz
= realsz
- 32 - 16; /* remove redzones */
179 size_t rightrz
= sz
- validsz
- 32; /* 16 bytes, plus whatever is left over */
180 struct fakestack_header
*hdr
= (struct fakestack_header
*)ret
;
182 kasan_poison(ret
, validsz
, leftrz
, rightrz
, ASAN_STACK_RZ
);
184 hdr
->site
= __builtin_return_address(0);
185 hdr
->realsz
= realsz
;
186 hdr
->sz_class
= sz_class
;
187 hdr
->flag
= FAKESTACK_ALLOCATED
;
188 ret
+= FAKESTACK_HEADER_SZ
;
190 *fakestack_flag_ptr(ret
, sz
) = &hdr
->flag
; /* back ptr to the slot */
191 struct fakestack_header_list
*head
= &kasan_get_thread_data(current_thread())->fakestack_head
;
192 LIST_INSERT_HEAD(head
, hdr
, list
);
200 kasan_fakestack_free(int sz_class
, uptr dst
, size_t realsz
)
202 if (ptr_is_on_stack(dst
)) {
206 assert(realsz
<= (fakestack_min
<< sz_class
));
207 assert(__asan_option_detect_stack_use_after_return
);
209 vm_size_t sz
= fakestack_min
<< sz_class
;
210 zone_t zone
= fakestack_zones
[sz_class
];
213 /* TODO: check the magic? */
215 dst
-= FAKESTACK_HEADER_SZ
;
216 sz
+= FAKESTACK_HEADER_SZ
;
218 struct fakestack_header
*hdr
= (struct fakestack_header
*)dst
;
219 assert(hdr
->sz_class
== sz_class
);
224 LIST_REMOVE(hdr
, list
);
226 kasan_free_internal((void **)&dst
, &sz
, KASAN_HEAP_FAKESTACK
, &zone
, realsz
, 1, FAKESTACK_QUARANTINE
);
228 zfree(zone
, (void *)dst
);
235 kasan_unpoison_fakestack(thread_t thread
)
237 if (!__asan_option_detect_stack_use_after_return
) {
244 thread_enter_fakestack();
246 struct fakestack_header_list
*head
= &kasan_get_thread_data(thread
)->fakestack_head
;
247 struct fakestack_header
*cur
;
248 LIST_FOREACH(cur
, head
, list
) {
249 if (cur
->flag
== FAKESTACK_ALLOCATED
) {
250 cur
->flag
= FAKESTACK_FREED
;
254 kasan_fakestack_gc(thread
);
255 thread_exit_fakestack();
260 kasan_init_fakestack(void)
262 /* allocate the fakestack zones */
263 for (int i
= 0; i
< FAKESTACK_NUM_SZCLASS
; i
++) {
265 unsigned long sz
= (fakestack_min
<< i
) + FAKESTACK_HEADER_SZ
;
266 size_t maxsz
= 256UL * 1024;
269 /* size classes 0..3 are much more common */
273 snprintf(fakestack_names
[i
], 16, "fakestack.%d", i
);
274 z
= zinit(sz
, maxsz
, sz
, fakestack_names
[i
]);
276 zone_change(z
, Z_NOCALLOUT
, TRUE
);
277 zone_change(z
, Z_EXHAUST
, TRUE
);
278 zone_change(z
, Z_EXPAND
, FALSE
);
279 zone_change(z
, Z_COLLECT
, FALSE
);
280 zone_change(z
, Z_KASAN_QUARANTINE
, FALSE
);
281 zfill(z
, maxsz
/ sz
);
282 fakestack_zones
[i
] = z
;
285 /* globally enable */
286 __asan_option_detect_stack_use_after_return
= 1;
289 #else /* FAKESTACK */
292 kasan_init_fakestack(void)
294 assert(__asan_option_detect_stack_use_after_return
== 0);
298 kasan_unpoison_fakestack(thread_t __unused thread
)
300 assert(__asan_option_detect_stack_use_after_return
== 0);
304 kasan_fakestack_alloc(int __unused sz_class
, size_t __unused realsz
)
306 assert(__asan_option_detect_stack_use_after_return
== 0);
311 kasan_fakestack_free(int __unused sz_class
, uptr __unused dst
, size_t __unused realsz
)
313 assert(__asan_option_detect_stack_use_after_return
== 0);
314 panic("fakestack_free called on non-FAKESTACK config\n");
319 void kasan_init_thread(struct kasan_thread_data
*td
)
321 td
->in_fakestack
= 0;
322 LIST_INIT(&td
->fakestack_head
);
325 #define FAKESTACK_DECLARE(szclass) \
326 uptr __asan_stack_malloc_##szclass(size_t sz) { return kasan_fakestack_alloc(szclass, sz); } \
327 void __asan_stack_free_##szclass(uptr dst, size_t sz) { kasan_fakestack_free(szclass, dst, sz); }
339 FAKESTACK_DECLARE(10)