xref: /xnu-12377.81.4/tests/ksancov.c (revision 043036a2b3718f7f0be807e2870f8f47d3fa0796)
1 /*
2  * Copyright (c) 2025 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 
29 #include <darwintest.h>
30 #include <fcntl.h>
31 #include <sys/types.h>
32 #include <sys/sysctl.h>
33 #include <IOKit/IOKitLib.h>
34 #include "test_utils.h"
35 
36 // Include the KSANCOV tool header
37 #include "../san/tools/ksancov.h"
38 
39 T_GLOBAL_META(
40 	T_META_RADAR_COMPONENT_NAME("xnu"),
41 	T_META_RADAR_COMPONENT_VERSION("kasan"),
42 	T_META_OWNER("a_fioraldi")
43 	);
44 
45 T_DECL(
46 	kcov_ksancov_mode_trace,
47 	"Ensure that KSANCOV can trace program counters when the kernel is compiled with instrumentation (KSANCOV=1)",
48 	// Only relevant on KASAN kernels with KCOV available
49 	T_META_REQUIRES_SYSCTL_EQ("kern.kcov.available", 1),
50 	// Only relevant on KASAN kernels compiled with SanitizerCoverage instrumentation
51 	T_META_REQUIRES_SYSCTL_EQ("kern.kcov.sancov_compiled", 1)
52 	) {
53 	int fd = ksancov_open();
54 	T_ASSERT_POSIX_SUCCESS(fd, "ksancov_open");
55 
56 	size_t max_entries = 64UL * 1024;
57 	int ret = ksancov_mode_trace(fd, max_entries);
58 	T_ASSERT_POSIX_SUCCESS(ret, "ksancov_mode_trace");
59 
60 	uintptr_t addr;
61 	size_t sz;
62 	ret = ksancov_map(fd, &addr, &sz);
63 	T_ASSERT_POSIX_SUCCESS(ret, "ksancov_map");
64 
65 	ksancov_trace_t *trace = (ksancov_trace_t *)addr;
66 
67 	ret = ksancov_thread_self(fd);
68 	T_ASSERT_POSIX_SUCCESS(ret, "ksancov_thread_self");
69 
70 	ksancov_reset(trace);
71 	ksancov_start(trace);
72 
73 	// trace the getppid syscall
74 	getppid();
75 
76 	ksancov_stop(trace);
77 
78 	// ksancov_trace_* contain useful assertions
79 	size_t head = ksancov_trace_head(trace);
80 	T_EXPECT_GT(head, (size_t)0, "Expected to have at least 1 program counter in the coverage trace");
81 	if (head > 0) {
82 		ksancov_trace_entry(trace, 0);
83 	}
84 
85 	ret = close(fd);
86 	T_ASSERT_POSIX_SUCCESS(ret, "close");
87 }
88 
89 T_DECL(
90 	kcov_ksancov_mode_trace_kext,
91 	"Ensure that KSANCOV can trace program counters of a kext bundle",
92 	// Only relevant on KASAN kernels with KCOV available
93 	T_META_REQUIRES_SYSCTL_EQ("kern.kcov.available", 1),
94 	// Can enable coverage collection of KEXTs only when on demand KSANCOV is enabled
95 	T_META_REQUIRES_SYSCTL_EQ("kern.kcov.od.support_enabled", 1),
96 	// Enable this test only on iPhone rdar://155524478
97 	T_META_ENABLED(TARGET_OS_IOS && !TARGET_OS_MACCATALYST && !TARGET_OS_SIMULATOR)
98 	) {
99 	io_service_t io_surface = IO_OBJECT_NULL;
100 	io_surface = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOSurfaceRoot"));
101 	T_ASSERT_NE(io_surface, IO_OBJECT_NULL, "Expected to find the IOSurface service");
102 
103 	int fd = ksancov_open();
104 	T_ASSERT_POSIX_SUCCESS(fd, "ksancov_open");
105 
106 	size_t max_entries = 64UL * 1024;
107 	int ret = ksancov_mode_trace(fd, max_entries);
108 	T_ASSERT_POSIX_SUCCESS(ret, "ksancov_mode_trace");
109 
110 	uintptr_t addr;
111 	size_t sz;
112 	ret = ksancov_map(fd, &addr, &sz);
113 	T_ASSERT_POSIX_SUCCESS(ret, "ksancov_map");
114 
115 	ksancov_trace_t *trace = (ksancov_trace_t *)addr;
116 
117 	ret = ksancov_thread_self(fd);
118 	T_ASSERT_POSIX_SUCCESS(ret, "ksancov_thread_self");
119 
120 	ret = ksancov_on_demand_set_gate(fd, "com.apple.iokit.IOSurface", true);
121 	T_ASSERT_POSIX_SUCCESS(ret, "ksancov_on_demand_set_gate");
122 	ksancov_reset(trace);
123 	ksancov_start(trace);
124 
125 	// trace the IOSurface IOServiceOpen/Close
126 	io_connect_t connect = IO_OBJECT_NULL;
127 	IOReturn ioret;
128 	ioret = IOServiceOpen(io_surface, mach_task_self(), 0, &connect);
129 	T_EXPECT_EQ(ioret, kIOReturnSuccess, "Unable to open the IOSurface userclient");
130 	if (ioret == kIOReturnSuccess) {
131 		IOServiceClose(connect);
132 	}
133 
134 	ksancov_stop(trace);
135 	ret = ksancov_on_demand_set_gate(fd, "com.apple.iokit.IOSurface", false);
136 	T_ASSERT_POSIX_SUCCESS(ret, "ksancov_on_demand_set_gate");
137 
138 	// ksancov_trace_* contain useful assertions
139 	size_t head = ksancov_trace_head(trace);
140 	T_EXPECT_GT(head, (size_t)0, "Expected to have at least 1 program counter in the coverage trace");
141 	if (head > 0) {
142 		ksancov_trace_entry(trace, 0);
143 	}
144 
145 	ret = close(fd);
146 	T_ASSERT_POSIX_SUCCESS(ret, "close");
147 }
148 
149 T_DECL(
150 	kcov_ksancov_mode_trace_cmp_kext,
151 	"Ensure that KSANCOV can trace program counters and comparisons of a kext bundle",
152 	// Only relevant on KASAN kernels with KCOV available
153 	T_META_REQUIRES_SYSCTL_EQ("kern.kcov.available", 1),
154 	// Can enable coverage collection of KEXTs only when on demand KSANCOV is enabled
155 	T_META_REQUIRES_SYSCTL_EQ("kern.kcov.od.support_enabled", 1),
156 	// Enable this test only on iPhone rdar://155524478
157 	T_META_ENABLED(TARGET_OS_IOS && !TARGET_OS_MACCATALYST && !TARGET_OS_SIMULATOR)
158 	) {
159 	io_service_t io_surface = IO_OBJECT_NULL;
160 	io_surface = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOSurfaceRoot"));
161 	T_ASSERT_NE(io_surface, IO_OBJECT_NULL, "Expected to find the IOSurface service");
162 
163 	int fd = ksancov_open();
164 	T_ASSERT_POSIX_SUCCESS(fd, "ksancov_open");
165 
166 	size_t max_entries = 64UL * 1024;
167 	int ret = ksancov_mode_trace(fd, max_entries);
168 	T_ASSERT_POSIX_SUCCESS(ret, "ksancov_mode_trace");
169 
170 	uintptr_t addr;
171 	size_t sz;
172 	ret = ksancov_map(fd, &addr, &sz);
173 	T_ASSERT_POSIX_SUCCESS(ret, "ksancov_map");
174 
175 	ksancov_trace_t *trace = (ksancov_trace_t *)addr;
176 
177 	ret = ksancov_cmps_mode_trace(fd, max_entries, true);
178 	T_ASSERT_POSIX_SUCCESS(ret, "ksancov_cmps_mode_trace");
179 
180 	ret = ksancov_cmps_map(fd, &addr, &sz);
181 	T_ASSERT_POSIX_SUCCESS(ret, "ksancov_cmps_map");
182 
183 	ksancov_trace_t *cmps_trace = (ksancov_trace_t *)addr;
184 
185 	ret = ksancov_thread_self(fd);
186 	T_ASSERT_POSIX_SUCCESS(ret, "ksancov_thread_self");
187 
188 	ret = ksancov_on_demand_set_gate(fd, "com.apple.iokit.IOSurface", true);
189 	T_ASSERT_POSIX_SUCCESS(ret, "ksancov_on_demand_set_gate");
190 	ksancov_reset(trace);
191 	ksancov_reset(cmps_trace);
192 	ksancov_start(trace);
193 	ksancov_start(cmps_trace);
194 
195 	// trace the IOSurface IOServiceOpen/Close
196 	io_connect_t connect = IO_OBJECT_NULL;
197 	IOReturn ioret;
198 	ioret = IOServiceOpen(io_surface, mach_task_self(), 0, &connect);
199 	T_EXPECT_EQ(ioret, kIOReturnSuccess, "Unable to open the IOSurface userclient");
200 	if (ioret == kIOReturnSuccess) {
201 		IOServiceClose(connect);
202 	}
203 
204 	ksancov_stop(trace);
205 	ksancov_stop(cmps_trace);
206 	ret = ksancov_on_demand_set_gate(fd, "com.apple.iokit.IOSurface", false);
207 	T_ASSERT_POSIX_SUCCESS(ret, "ksancov_on_demand_set_gate");
208 
209 	// ksancov_trace_* contain useful assertions
210 	size_t head = ksancov_trace_head(trace);
211 	T_EXPECT_GT(head, (size_t)0, "Expected to have at least 1 program counter in the coverage trace");
212 	if (head > 0) {
213 		ksancov_trace_entry(trace, 0);
214 	}
215 
216 	head = ksancov_trace_head(cmps_trace);
217 	T_EXPECT_GT(head, (size_t)0, "Expected to have at least 1 comparison in the comparisons trace");
218 
219 	size_t i;
220 	for (i = 0; i < head;) {
221 		ksancov_cmps_trace_ent_t *entry = ksancov_cmps_trace_entry(cmps_trace, i);
222 		if (KCOV_CMP_IS_FUNC(entry->type)) {
223 			size_t space = ksancov_cmps_trace_func_space(entry->len1_func, entry->len2_func);
224 			i += space / sizeof(ksancov_cmps_trace_ent_t);
225 		} else {
226 			++i;
227 		}
228 	}
229 	T_EXPECT_EQ(i, head, "Cursor went past the end of the trace");
230 
231 	ret = close(fd);
232 	T_ASSERT_POSIX_SUCCESS(ret, "close");
233 }
234 
235 T_DECL(
236 	kcov_ksancov_testcases,
237 	"Test the testcases buffer feature in KSANCOV",
238 	// Only relevant on KASAN kernels with KCOV available
239 	T_META_REQUIRES_SYSCTL_EQ("kern.kcov.available", 1)
240 	) {
241 	int fd = ksancov_open();
242 	if (fd < 0) {
243 		T_SKIP("Ksancov not available, skipping...");
244 		return;
245 	}
246 
247 	size_t max_entries = 32;
248 	int ret = ksancov_mode_trace(fd, max_entries);
249 	T_ASSERT_POSIX_SUCCESS(ret, "ksancov_mode_trace");
250 
251 	size_t num_testcases = 2;
252 	ret = ksancov_testcases(fd, num_testcases);
253 	T_ASSERT_POSIX_SUCCESS(ret, "ksancov_testcases");
254 
255 	uintptr_t addr;
256 	size_t sz;
257 	ret = ksancov_testcases_map(fd, &addr, &sz);
258 	T_ASSERT_POSIX_SUCCESS(ret, "ksancov_testcases_map");
259 
260 	T_EXPECT_EQ(sz, sizeof(ksancov_serialized_testcases_t) + sizeof(ksancov_serialized_testcase_t) * num_testcases, "Testcases mapped size mismatch");
261 	ksancov_serialized_testcases_t *testcases = (ksancov_serialized_testcases_t *)addr;
262 
263 	ret = ksancov_thread_self(fd);
264 	T_ASSERT_POSIX_SUCCESS(ret, "ksancov_thread_self");
265 
266 	T_ASSERT_EQ(sizeof(testcases->list[testcases->head].buffer), (size_t)KSANCOV_SERIALIZED_TESTCASE_BYTES, "Buffer size not KSANCOV_SERIALIZED_TESTCASE_BYTES");
267 	testcases->list[testcases->head].buffer[0] = 'f';
268 	testcases->list[testcases->head].buffer[1] = 'o';
269 	testcases->list[testcases->head].buffer[2] = 'o';
270 	testcases->list[testcases->head].buffer[3] = 0;
271 	testcases->list[testcases->head].size = 4;
272 	testcases->head = (testcases->head + 1) % num_testcases;
273 
274 	ret = ksancov_testcases_log(fd);
275 	// The serial log can fail if serial=0x1 or debug=0x2 are not set in the boot args
276 
277 	ret = close(fd);
278 	T_ASSERT_POSIX_SUCCESS(ret, "close");
279 }
280