1 /*
2 * Copyright (c) 2020 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 #include <string.h>
29 #include <stdbool.h>
30 #include <sys/sysctl.h>
31 #include <kern/cpu_number.h>
32 #include <kern/cpu_data.h>
33 #include <libkern/libkern.h>
34 #include <os/atomic_private.h>
35 #include <vm/pmap.h>
36 #include <machine/machine_routines.h>
37
38 #include <san/kcov.h>
39 #include <san/kcov_data.h>
40
41 #include <san/kcov_stksz.h>
42 #include <san/kcov_stksz_data.h>
43
44 #include <san/kcov_ksancov.h>
45 #include <san/kcov_ksancov_data.h>
46
47 /* Global flag that enables the sanitizer hook. */
48 static _Atomic unsigned int kcov_enabled = 0;
49
50
51 /*
52 * Sysctl interface to coverage sanitizer.
53 */
54 SYSCTL_DECL(_kern_kcov);
55 SYSCTL_NODE(_kern, OID_AUTO, kcov, CTLFLAG_RW | CTLFLAG_LOCKED, 0, "kcov");
56 SYSCTL_COMPAT_INT(_kern_kcov, OID_AUTO, available, CTLFLAG_RD, NULL, 1, "");
57
58 /*
59 * kern.kcov.sancov_compiled is 1 when XNU itself is compiled with SanitizerCoverage
60 */
61 #if __has_feature(coverage_sanitizer)
62 SYSCTL_COMPAT_INT(_kern_kcov, OID_AUTO, sancov_compiled, CTLFLAG_RD, NULL, 1, "");
63 #else
64 SYSCTL_COMPAT_INT(_kern_kcov, OID_AUTO, sancov_compiled, CTLFLAG_RD, NULL, 0, "");
65 #endif
66
67 /*
68 * Coverage sanitizer bootstrap.
69 *
70 * A compiler will add hooks almost in any basic block in the kernel. However it is
71 * not safe to call hook from some of the contexts. To make this safe it would require
72 * precise denylist of all unsafe sources. Which results in high maintenance costs.
73 *
74 * To avoid this we bootsrap the coverage sanitizer in phases:
75 *
76 * 1. Kernel starts with globaly disabled coverage sanitizer. At this point the hook
77 * can access safely only global variables.
78 * 2. The boot cpu has allocated/configured per-cpu data. At this point the hook can
79 * use per-cpu data by using current_* but only on the boot cpu.
80 *
81 * ... From this point we can start recording on boot cpu
82 *
83 * 3. Additional CPUs are added by kext. We rely on the fact that default value of
84 * per-cpu variable is 0. The assumption here is that some other (already configured)
85 * cpu is running the bootsrap of secondary CPU which is safe. Once secondary gets
86 * configured the boostrap originator enables its converage sanitizer by writing
87 * secondary's per-cpu data.
88 *
89 * To make this step safe, it is required to maintain denylist that contains CPU
90 * bootstrap code to avoid firing hook from unsupported context.
91 *
92 * ... From this point all CPUs can execute the hook correctly.
93 *
94 * This allows stack size monitoring during early boot. For all other cases we simply
95 * boot with global set to 0 waiting for a client to actually enable sanitizer.
96 */
97
98 /*
99 * 1. & 2. enabling step. Must be called *after* per-cpu data are set up.
100 */
101 __startup_func
102 static void
kcov_init(void)103 kcov_init(void)
104 {
105 /* Master CPU is fully setup at this point so just enable coverage tracking. */
106 ksancov_init();
107 current_kcov_data()->kcd_enabled = 1;
108 }
109 STARTUP(EARLY_BOOT, STARTUP_RANK_LAST, kcov_init);
110
111 /*
112 * 3. secondary CPU. Called on bootstrap originator after secondary is ready.
113 */
114 void
kcov_start_cpu(int cpuid)115 kcov_start_cpu(int cpuid)
116 {
117 /* No need to use atomics as we don't need to be so precise here. */
118 cpu_kcov_data(cpuid)->kcd_enabled = 1;
119 }
120
121 void
kcov_enable(void)122 kcov_enable(void)
123 {
124 os_atomic_add(&kcov_enabled, 1, relaxed);
125 }
126
127 void
kcov_disable(void)128 kcov_disable(void)
129 {
130 os_atomic_sub(&kcov_enabled, 1, relaxed);
131 }
132
133
134 /*
135 * Disable coverage sanitizer recording for given thread.
136 */
137 static void
kcov_disable_thread(kcov_thread_data_t * data)138 kcov_disable_thread(kcov_thread_data_t *data)
139 {
140 data->ktd_disabled++;
141 }
142
143
144 /*
145 * Enable coverage sanitizer recording for given thread.
146 */
147 static void
kcov_enable_thread(kcov_thread_data_t * data)148 kcov_enable_thread(kcov_thread_data_t *data)
149 {
150 data->ktd_disabled--;
151 }
152
153
154 /*
155 * Called when system enters panic code path with no return. There is no point in tracking
156 * stack usage and delay (and possibly break) the coredump code.
157 */
158 void
kcov_panic_disable(void)159 kcov_panic_disable(void)
160 {
161 printf("KCOV: Disabling coverage tracking. System panicking.\n");
162 /* Force disable the sanitizer hook. */
163 os_atomic_store(&kcov_enabled, 0, relaxed);
164 }
165
166
167 /* Initialize per-thread sanitizer data for each new kernel thread. */
168 void
kcov_init_thread(kcov_thread_data_t * data)169 kcov_init_thread(kcov_thread_data_t *data)
170 {
171 data->ktd_disabled = 0;
172
173 kcov_ksancov_init_thread(&data->ktd_device);
174 kcov_stksz_init_thread(&data->ktd_stksz);
175 }
176
177 /* Shared prologue between trace functions */
178 static kcov_thread_data_t *
trace_prologue(void)179 trace_prologue(void)
180 {
181 /* Check the global flag for the case no recording is enabled. */
182 if (__probable(os_atomic_load(&kcov_enabled, relaxed) == 0)) {
183 return NULL;
184 }
185
186 /*
187 * rdar://145659776
188 * If PAN is disabled we cannot safely re-enable preemption after disabling it.
189 * The proper way to do this in a generic way is to check here for PAN and bail ot
190 * if (__improbable(__builtin_arm_rsr("pan") == 0))
191 *
192 * The issue with this solution is the performance cost of reading the MSR for each
193 * trace point, so PAN disabled functions are included in the baclklist instead
194 * (see kcov-blacklist-arm64).
195 */
196
197 /* Per-cpu area access. Must happen with disabled interrupts/preemtion. */
198 disable_preemption();
199
200 if (!current_kcov_data()->kcd_enabled) {
201 enable_preemption();
202 return NULL;
203 }
204
205 /* No support for PPL. */
206 if (pmap_in_ppl()) {
207 enable_preemption();
208 return NULL;
209 }
210 /* Interrupt context not supported. */
211 if (ml_at_interrupt_context()) {
212 enable_preemption();
213 return NULL;
214 }
215
216 thread_t th = current_thread();
217 if (__improbable(th == THREAD_NULL)) {
218 enable_preemption();
219 return NULL;
220 }
221
222 /* This thread does not want to be traced. */
223 kcov_thread_data_t *data = kcov_get_thread_data(th);
224 if (__improbable(data->ktd_disabled) != 0) {
225 enable_preemption();
226 return NULL;
227 }
228
229 /* Enable preemption as we are no longer accessing per-cpu data. */
230 enable_preemption();
231
232 return data;
233 }
234
235 /*
236 * This is the core of the coverage recording.
237 *
238 * A compiler inlines this function into every place eligible for instrumentation.
239 * Every modification is very risky as added code may be called from unexpected
240 * contexts (for example per-cpu data access).
241 *
242 * Do not call anything unnecessary before ksancov_disable() as that will cause
243 * recursion. Update denylist after any such change.
244 *
245 * Every complex code here may have impact on the overall performance. This function
246 * is called for every edge in the kernel and that means multiple times through a
247 * single function execution.
248 */
249 static void
trace_pc_guard(uint32_t __unused * guardp,void __unused * caller,uintptr_t __unused sp)250 trace_pc_guard(uint32_t __unused *guardp, void __unused *caller, uintptr_t __unused sp)
251 {
252 kcov_ksancov_trace_guard(guardp, caller);
253
254 kcov_thread_data_t *data = trace_prologue();
255 if (data == NULL) {
256 return;
257 }
258
259 /* It is now safe to call back to kernel from this thread without recursing in the hook itself. */
260 kcov_disable_thread(data);
261
262 kcov_stksz_update_stack_size(th, data, caller, sp);
263 kcov_ksancov_trace_pc(data, guardp, caller, sp);
264
265 kcov_enable_thread(data);
266 }
267
268 /*
269 * Coverage Sanitizer ABI implementation.
270 */
271
272
273 void
__sanitizer_cov_trace_pc_indirect(void * __unused callee)274 __sanitizer_cov_trace_pc_indirect(void * __unused callee)
275 {
276 /* No indirect call recording support at this moment. */
277 return;
278 }
279
280
281 __attribute__((nodebug))
282 void
__sanitizer_cov_trace_pc(void)283 __sanitizer_cov_trace_pc(void)
284 {
285 uintptr_t sp = (uintptr_t)&sp;
286 trace_pc_guard(NULL, __builtin_return_address(0), sp);
287 }
288
289
290 __attribute__((nodebug))
291 void
__sanitizer_cov_trace_pc_guard(uint32_t __unused * guardp)292 __sanitizer_cov_trace_pc_guard(uint32_t __unused *guardp)
293 {
294 uintptr_t sp = (uintptr_t)&sp;
295 trace_pc_guard(guardp, __builtin_return_address(0), sp);
296 }
297
298
299 void
__sanitizer_cov_trace_pc_guard_init(uint32_t __unused * start,uint32_t __unused * stop)300 __sanitizer_cov_trace_pc_guard_init(uint32_t __unused *start, uint32_t __unused *stop)
301 {
302 kcov_ksancov_trace_pc_guard_init(start, stop);
303 }
304
305
306 void
__sanitizer_cov_pcs_init(uintptr_t __unused * start,uintptr_t __unused * stop)307 __sanitizer_cov_pcs_init(uintptr_t __unused *start, uintptr_t __unused *stop)
308 {
309 kcov_ksancov_pcs_init(start, stop);
310 }
311
312 static void
trace_cmp(uint32_t __unused type,uint64_t __unused arg1,uint64_t __unused arg2,void __unused * caller)313 trace_cmp(uint32_t __unused type, uint64_t __unused arg1, uint64_t __unused arg2, void __unused *caller)
314 {
315 kcov_thread_data_t *data = trace_prologue();
316 if (data == NULL) {
317 return;
318 }
319
320 /* It is now safe to call back to kernel from this thread without recursing in the hook itself. */
321 kcov_disable_thread(data);
322
323 kcov_ksancov_trace_cmp(data, type, arg1, arg2, caller);
324
325 kcov_enable_thread(data);
326 }
327
328 void
__sanitizer_cov_trace_cmp1(uint8_t arg1,uint8_t arg2)329 __sanitizer_cov_trace_cmp1(uint8_t arg1, uint8_t arg2)
330 {
331 trace_cmp(KCOV_CMP_SIZE1, arg1, arg2, __builtin_return_address(0));
332 }
333
334 void
__sanitizer_cov_trace_cmp2(uint16_t arg1,uint16_t arg2)335 __sanitizer_cov_trace_cmp2(uint16_t arg1, uint16_t arg2)
336 {
337 trace_cmp(KCOV_CMP_SIZE2, arg1, arg2, __builtin_return_address(0));
338 }
339
340 void
__sanitizer_cov_trace_cmp4(uint32_t arg1,uint32_t arg2)341 __sanitizer_cov_trace_cmp4(uint32_t arg1, uint32_t arg2)
342 {
343 trace_cmp(KCOV_CMP_SIZE4, arg1, arg2, __builtin_return_address(0));
344 }
345
346 void
__sanitizer_cov_trace_cmp8(uint64_t arg1,uint64_t arg2)347 __sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2)
348 {
349 trace_cmp(KCOV_CMP_SIZE8, arg1, arg2, __builtin_return_address(0));
350 }
351
352 void
__sanitizer_cov_trace_const_cmp1(uint8_t arg1,uint8_t arg2)353 __sanitizer_cov_trace_const_cmp1(uint8_t arg1, uint8_t arg2)
354 {
355 trace_cmp(KCOV_CMP_SIZE1 | KCOV_CMP_CONST, arg1, arg2, __builtin_return_address(0));
356 }
357
358 void
__sanitizer_cov_trace_const_cmp2(uint16_t arg1,uint16_t arg2)359 __sanitizer_cov_trace_const_cmp2(uint16_t arg1, uint16_t arg2)
360 {
361 trace_cmp(KCOV_CMP_SIZE2 | KCOV_CMP_CONST, arg1, arg2, __builtin_return_address(0));
362 }
363
364 void
__sanitizer_cov_trace_const_cmp4(uint32_t arg1,uint32_t arg2)365 __sanitizer_cov_trace_const_cmp4(uint32_t arg1, uint32_t arg2)
366 {
367 trace_cmp(KCOV_CMP_SIZE4 | KCOV_CMP_CONST, arg1, arg2, __builtin_return_address(0));
368 }
369
370 void
__sanitizer_cov_trace_const_cmp8(uint64_t arg1,uint64_t arg2)371 __sanitizer_cov_trace_const_cmp8(uint64_t arg1, uint64_t arg2)
372 {
373 trace_cmp(KCOV_CMP_SIZE8 | KCOV_CMP_CONST, arg1, arg2, __builtin_return_address(0));
374 }
375
376 void
__sanitizer_cov_trace_switch(uint64_t val,uint64_t * cases)377 __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases)
378 {
379 void *ret = __builtin_return_address(0);
380
381 uint32_t type;
382 switch (cases[1]) {
383 case 8:
384 type = KCOV_CMP_SIZE1 | KCOV_CMP_CONST;
385 break;
386 case 16:
387 type = KCOV_CMP_SIZE2 | KCOV_CMP_CONST;
388 break;
389 case 32:
390 type = KCOV_CMP_SIZE4 | KCOV_CMP_CONST;
391 break;
392 case 64:
393 type = KCOV_CMP_SIZE8 | KCOV_CMP_CONST;
394 break;
395 default:
396 return;
397 }
398
399 uint64_t i;
400 uint64_t count = cases[0];
401
402 for (i = 0; i < count; i++) {
403 trace_cmp(type, cases[i + 2], val, ret);
404 }
405 }
406
407 void
kcov_trace_cmp_func(void * caller_pc,uint32_t type,const void * s1,size_t s1len,const void * s2,size_t s2len,bool always_log)408 kcov_trace_cmp_func(void *caller_pc, uint32_t type, const void *s1, size_t s1len, const void *s2, size_t s2len, bool always_log)
409 {
410 kcov_thread_data_t *data = trace_prologue();
411 if (data == NULL) {
412 return;
413 }
414
415 /* It is now safe to call back to kernel from this thread without recursing in the hook itself. */
416 kcov_disable_thread(data);
417
418 kcov_ksancov_trace_cmp_func(data, type, s1, s1len, s2, s2len, caller_pc, always_log);
419
420 kcov_enable_thread(data);
421 }
422