xref: /xnu-12377.41.6/san/coverage/kcov.c (revision bbb1b6f9e71b8cdde6e5cd6f4841f207dee3d828)
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