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