xref: /xnu-8796.121.2/san/coverage/kcov_stksz.c (revision c54f35ca767986246321eb901baf8f5ff7923f6a)
1 /*
2  * Copyright (c) 2019 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  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
26  */
27 
28 #include <sys/kdebug.h>
29 #include <sys/sysctl.h>
30 #include <san/kcov.h>
31 #include <san/kcov_data.h>
32 #include <san/kcov_stksz.h>
33 
34 #include <mach/vm_param.h>
35 
36 #ifdef CONFIG_DTRACE
37 #include <mach/sdt.h>
38 #endif
39 
40 
41 /* Enables stack size recording. */
42 static uint32_t stksz_enabled = 0;
43 
44 /* Stack size that represents the watermark. */
45 static uint32_t stksz_threshold = KERNEL_STACK_SIZE + 1; /* Larger than stack == disabled. */
46 
47 /* Stack size increment to trigger delta event. */
48 static uint32_t stksz_delta = 0;
49 
50 
51 /*
52  * Sysctl interface for stack size monitoring.
53  */
54 
55 SYSCTL_DECL(_kern_kcov);
56 SYSCTL_NODE(_kern_kcov, OID_AUTO, stksz, CTLFLAG_RW | CTLFLAG_LOCKED, 0, "stksz");
57 SYSCTL_INT(_kern_kcov_stksz, OID_AUTO, threshold, CTLFLAG_RW | CTLFLAG_LOCKED, &stksz_threshold,
58     KERNEL_STACK_SIZE + 1, "stack size threshold");
59 SYSCTL_INT(_kern_kcov_stksz, OID_AUTO, delta, CTLFLAG_RW | CTLFLAG_LOCKED, &stksz_delta,
60     KERNEL_STACK_SIZE + 1, "stack delta");
61 
62 static int
63 sysctl_kcov_stksz_enabled SYSCTL_HANDLER_ARGS
64 {
65 	int err;
66 	int value = *(int *)arg1;
67 
68 	err = sysctl_io_number(req, value, sizeof(value), &value, NULL);
69 	if (err) {
70 		return err;
71 	}
72 
73 	if (req->newptr) {
74 		if (!(value == 0 || value == 1)) {
75 			return ERANGE;
76 		}
77 
78 		/* No change. */
79 		if (value == stksz_enabled) {
80 			return 0;
81 		}
82 
83 		/* enable/disable ksancov global switch when required. */
84 		if (stksz_enabled) {
85 			kcov_disable();
86 		} else {
87 			kcov_enable();
88 		}
89 		stksz_enabled = value;
90 	}
91 
92 	return 0;
93 }
94 
95 SYSCTL_PROC(_kern_kcov_stksz, OID_AUTO, enabled, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED,
96     &stksz_enabled, 0, sysctl_kcov_stksz_enabled, "I", "stack size recording enabled");
97 
98 
99 /* Constants to make code below simpler. */
100 const uint32_t debugid_above = MACHDBG_CODE(DBG_MACH_KCOV, KCOV_STKSZ_THRESHOLD_ABOVE);
101 const uint32_t debugid_below = MACHDBG_CODE(DBG_MACH_KCOV, KCOV_STKSZ_THRESHOLD_BELOW);
102 const uint32_t debugid_delta = MACHDBG_CODE(DBG_MACH_KCOV, KCOV_STKSZ_DELTA);
103 
104 
105 void
kcov_stksz_init_thread(kcov_stksz_thread_t * data)106 kcov_stksz_init_thread(kcov_stksz_thread_t *data)
107 {
108 	data->kst_pc = 0;
109 	data->kst_stksz = 0;
110 	data->kst_stksz_prev = 0;
111 	data->kst_stack = 0;
112 	data->kst_th_above = 0;
113 }
114 
115 
116 void
kcov_stksz_update_stack_size(thread_t th,kcov_thread_data_t * kcdata,void * caller,uintptr_t sp)117 kcov_stksz_update_stack_size(thread_t th, kcov_thread_data_t *kcdata, void *caller, uintptr_t sp)
118 {
119 	if (!stksz_enabled) {
120 		return;
121 	}
122 
123 	vm_offset_t stack_base = kcov_stksz_get_thread_stkbase(th);
124 	vm_offset_t stack_size = kcov_stksz_get_thread_stksize(th);
125 
126 	/* Ensure that we are in thread's stack. */
127 	if (sp < stack_base || sp >= stack_base + stack_size) {
128 		return;
129 	}
130 
131 	/* Update kernel thread statistics. */
132 	kcov_stksz_thread_t *data = &kcdata->ktd_stksz;
133 	data->kst_pc = (uintptr_t)caller;
134 	data->kst_stksz_prev = data->kst_stksz;
135 	data->kst_stksz = (uint32_t)(stack_base + stack_size - sp);
136 
137 	/* Fire threshold related events. */
138 	if (!data->kst_th_above && data->kst_stksz > stksz_threshold) {
139 		data->kst_th_above = true;
140 
141 #if CONFIG_DTRACE
142 		if (dtrace_get_thread_inprobe(th) == 0) {
143 			DTRACE_KCOV1(stksz__threshold__above, uint32_t, data->kst_stksz);
144 		}
145 #endif
146 		if (kdebug_enable && kdebug_debugid_enabled(debugid_above)) {
147 			KDBG(debugid_above, caller, data->kst_stksz);
148 		}
149 	} else if (data->kst_th_above && data->kst_stksz < stksz_threshold) {
150 		data->kst_th_above = false;
151 
152 #if CONFIG_DTRACE
153 		if (dtrace_get_thread_inprobe(th) == 0) {
154 			DTRACE_KCOV1(stksz__threshold__below, uint32_t, data->kst_stksz);
155 		}
156 #endif
157 		if (kdebug_enable && kdebug_debugid_enabled(debugid_below)) {
158 			KDBG(debugid_below, caller, data->kst_stksz);
159 		}
160 	}
161 
162 	/* Fire delta related events. */
163 	if (stksz_delta > 0 && data->kst_stksz > data->kst_stksz_prev) {
164 		uint32_t delta = data->kst_stksz - data->kst_stksz_prev;
165 		if (delta > stksz_delta) {
166 #if CONFIG_DTRACE
167 			if (dtrace_get_thread_inprobe(th) == 0) {
168 				DTRACE_KCOV1(stksz__delta, uint32_t, delta);
169 			}
170 #endif /* CONFIG_DTRACE */
171 			if (kdebug_enable && kdebug_debugid_enabled(debugid_delta)) {
172 				KDBG(debugid_delta, caller, delta);
173 			}
174 		}
175 	}
176 }
177