xref: /freebsd/tests/sys/kern/kcov.c (revision 8df8b2d3e51d1b816201d8a1fe8bc29fe192e562)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2018, 2019 Andrew Turner
5  *
6  * This software was developed by SRI International and the University of
7  * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237
8  * ("CTSRD"), as part of the DARPA CRASH research programme.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34 
35 #include <sys/param.h>
36 #include <sys/ioctl.h>
37 #include <sys/kcov.h>
38 #include <sys/mman.h>
39 
40 #include <machine/atomic.h>
41 
42 #include <fcntl.h>
43 #include <pthread.h>
44 #include <semaphore.h>
45 
46 #include <atf-c.h>
47 
48 static const char *modes[] = {
49     "PC tracing",
50     "comparison tracing",
51 };
52 
53 static int
54 open_kcov(void)
55 {
56 	int fd;
57 
58 	fd = open("/dev/kcov", O_RDWR);
59 	if (fd == -1)
60 		atf_tc_skip("Failed to open /dev/kcov");
61 
62 	return (fd);
63 }
64 
65 ATF_TC_WITHOUT_HEAD(kcov_bufsize);
66 ATF_TC_BODY(kcov_bufsize, tc)
67 {
68 	int fd;
69 
70 	fd = open_kcov();
71 
72 	ATF_CHECK(ioctl(fd, KIOSETBUFSIZE, 0) == -1);
73 	ATF_CHECK(ioctl(fd, KIOSETBUFSIZE, 1) == -1);
74 	ATF_CHECK(ioctl(fd, KIOSETBUFSIZE, 2) == 0);
75 	ATF_CHECK(ioctl(fd, KIOSETBUFSIZE, 2) == -1);
76 
77 	close(fd);
78 }
79 
80 ATF_TC_WITHOUT_HEAD(kcov_mmap);
81 ATF_TC_BODY(kcov_mmap, tc)
82 {
83 	void *data1, *data2;
84 	int fd;
85 
86 	fd = open_kcov();
87 
88 	ATF_CHECK(mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
89 	    fd, 0) == MAP_FAILED);
90 
91 	ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE,
92 	    2 * PAGE_SIZE / KCOV_ENTRY_SIZE) == 0);
93 
94 	ATF_CHECK(mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
95 	    fd, 0) == MAP_FAILED);
96 	ATF_CHECK(mmap(NULL, 3 * PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
97 	    fd, 0) == MAP_FAILED);
98 	ATF_REQUIRE((data1 = mmap(NULL, 2 * PAGE_SIZE, PROT_READ | PROT_WRITE,
99 	    MAP_SHARED, fd, 0)) != MAP_FAILED);
100 	ATF_REQUIRE((data2 = mmap(NULL, 2 * PAGE_SIZE, PROT_READ | PROT_WRITE,
101 	    MAP_SHARED, fd, 0)) != MAP_FAILED);
102 
103 	*(uint64_t *)data1 = 0x123456789abcdeful;
104 	ATF_REQUIRE(*(uint64_t *)data2 == 0x123456789abcdefull);
105 	*(uint64_t *)data2 = 0xfedcba9876543210ul;
106 	ATF_REQUIRE(*(uint64_t *)data1 == 0xfedcba9876543210ull);
107 
108 	munmap(data1, 2 * PAGE_SIZE);
109 	munmap(data2, 2 * PAGE_SIZE);
110 
111 	close(fd);
112 }
113 
114 /* This shouldn't panic */
115 ATF_TC_WITHOUT_HEAD(kcov_mmap_no_munmap);
116 ATF_TC_BODY(kcov_mmap_no_munmap, tc)
117 {
118 	int fd;
119 
120 	fd = open_kcov();
121 
122 	ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE, PAGE_SIZE / KCOV_ENTRY_SIZE) == 0);
123 
124 	ATF_CHECK(mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
125 	    fd, 0) != MAP_FAILED);
126 
127 	close(fd);
128 }
129 
130 ATF_TC_WITHOUT_HEAD(kcov_mmap_no_munmap_no_close);
131 ATF_TC_BODY(kcov_mmap_no_munmap_no_close, tc)
132 {
133 	int fd;
134 
135 	fd = open_kcov();
136 
137 	ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE, PAGE_SIZE / KCOV_ENTRY_SIZE) == 0);
138 
139 	ATF_CHECK(mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
140 	    fd, 0) != MAP_FAILED);
141 }
142 
143 static sem_t sem1, sem2;
144 
145 static void *
146 kcov_mmap_enable_thread(void *data)
147 {
148 	int fd;
149 
150 	fd = open_kcov();
151 	*(int *)data = fd;
152 
153 	ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE, PAGE_SIZE / KCOV_ENTRY_SIZE) == 0);
154 	ATF_CHECK(mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
155 	    fd, 0) != MAP_FAILED);
156 	ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_PC) == 0);
157 
158 	sem_post(&sem1);
159 	sem_wait(&sem2);
160 
161 	return (NULL);
162 }
163 
164 ATF_TC_WITHOUT_HEAD(kcov_mmap_enable_thread_close);
165 ATF_TC_BODY(kcov_mmap_enable_thread_close, tc)
166 {
167 	pthread_t thread;
168 	int fd;
169 
170 	sem_init(&sem1, 0, 0);
171 	sem_init(&sem2, 0, 0);
172 	pthread_create(&thread, NULL,
173 	    kcov_mmap_enable_thread, &fd);
174 	sem_wait(&sem1);
175 	close(fd);
176 	sem_post(&sem2);
177 	pthread_join(thread, NULL);
178 }
179 
180 ATF_TC_WITHOUT_HEAD(kcov_enable);
181 ATF_TC_BODY(kcov_enable, tc)
182 {
183 	int fd;
184 
185 	fd = open_kcov();
186 
187 	ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_PC) == -1);
188 
189 	ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE, PAGE_SIZE / KCOV_ENTRY_SIZE) == 0);
190 
191 	/* We need to enable before disable */
192 	ATF_CHECK(ioctl(fd, KIODISABLE, 0) == -1);
193 
194 	/* Check enabling works only with a valid trace method */
195 	ATF_CHECK(ioctl(fd, KIOENABLE, -1) == -1);
196 	ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_PC) == 0);
197 	ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_PC) == -1);
198 	ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_CMP) == -1);
199 
200 	/* Disable should only be called once */
201 	ATF_CHECK(ioctl(fd, KIODISABLE, 0) == 0);
202 	ATF_CHECK(ioctl(fd, KIODISABLE, 0) == -1);
203 
204 	/* Re-enabling should also work */
205 	ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_CMP) == 0);
206 	ATF_CHECK(ioctl(fd, KIODISABLE, 0) == 0);
207 
208 	close(fd);
209 }
210 
211 ATF_TC_WITHOUT_HEAD(kcov_enable_no_disable);
212 ATF_TC_BODY(kcov_enable_no_disable, tc)
213 {
214 	int fd;
215 
216 	fd = open_kcov();
217 	ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE, PAGE_SIZE / KCOV_ENTRY_SIZE) == 0);
218 	ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_PC) == 0);
219 	close(fd);
220 }
221 
222 ATF_TC_WITHOUT_HEAD(kcov_enable_no_disable_no_close);
223 ATF_TC_BODY(kcov_enable_no_disable_no_close, tc)
224 {
225 	int fd;
226 
227 	fd = open_kcov();
228 	ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE, PAGE_SIZE / KCOV_ENTRY_SIZE) == 0);
229 	ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_PC) == 0);
230 }
231 
232 static void *
233 common_head(int *fdp)
234 {
235 	void *data;
236 	int fd;
237 
238 	fd = open_kcov();
239 
240 	ATF_REQUIRE_MSG(ioctl(fd, KIOSETBUFSIZE,
241 	    PAGE_SIZE / KCOV_ENTRY_SIZE) == 0,
242 	    "Unable to set the kcov buffer size");
243 
244 	data = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
245 	ATF_REQUIRE_MSG(data != MAP_FAILED, "Unable to mmap the kcov buffer");
246 
247 	*fdp = fd;
248 	return (data);
249 }
250 
251 static void
252 common_tail(int fd, void *data)
253 {
254 
255 	ATF_REQUIRE_MSG(munmap(data, PAGE_SIZE) == 0,
256 	    "Unable to unmap the kcov buffer");
257 
258 	close(fd);
259 }
260 
261 static void
262 basic_test(u_int mode)
263 {
264 	uint64_t *buf;
265 	int fd;
266 
267 	buf = common_head(&fd);
268 	ATF_REQUIRE_MSG(ioctl(fd, KIOENABLE, mode) == 0,
269 	    "Unable to enable kcov %s",
270 	    mode < nitems(modes) ? modes[mode] : "unknown mode");
271 
272 	atomic_store_64(&buf[0], 0);
273 
274 	sleep(0);
275 	ATF_REQUIRE_MSG(atomic_load_64(&buf[0]) != 0, "No records found");
276 
277 	ATF_REQUIRE_MSG(ioctl(fd, KIODISABLE, 0) == 0,
278 	    "Unable to disable kcov");
279 
280 	common_tail(fd, buf);
281 }
282 
283 ATF_TC_WITHOUT_HEAD(kcov_basic_pc);
284 ATF_TC_BODY(kcov_basic_pc, tc)
285 {
286 	basic_test(KCOV_MODE_TRACE_PC);
287 }
288 
289 ATF_TC_WITHOUT_HEAD(kcov_basic_cmp);
290 ATF_TC_BODY(kcov_basic_cmp, tc)
291 {
292 	basic_test(KCOV_MODE_TRACE_CMP);
293 }
294 
295 static void *
296 thread_test_helper(void *ptr)
297 {
298 	uint64_t *buf = ptr;
299 
300 	atomic_store_64(&buf[0], 0);
301 	sleep(0);
302 	ATF_REQUIRE_MSG(atomic_load_64(&buf[0]) == 0,
303 	    "Records changed in blocked thread");
304 
305 	return (NULL);
306 }
307 
308 static void
309 thread_test(u_int mode)
310 {
311 	pthread_t thread;
312 	uint64_t *buf;
313 	int fd;
314 
315 	buf = common_head(&fd);
316 
317 	ATF_REQUIRE_MSG(ioctl(fd, KIOENABLE, mode) == 0,
318 	    "Unable to enable kcov %s",
319 	    mode < nitems(modes) ? modes[mode] : "unknown mode");
320 
321 	pthread_create(&thread, NULL, thread_test_helper, buf);
322 	pthread_join(thread, NULL);
323 
324 	ATF_REQUIRE_MSG(ioctl(fd, KIODISABLE, 0) == 0,
325 	    "Unable to disable kcov");
326 
327 	common_tail(fd, buf);
328 }
329 
330 ATF_TC_WITHOUT_HEAD(kcov_thread_pc);
331 ATF_TC_BODY(kcov_thread_pc, tc)
332 {
333 	thread_test(KCOV_MODE_TRACE_PC);
334 }
335 
336 ATF_TC_WITHOUT_HEAD(kcov_thread_cmp);
337 ATF_TC_BODY(kcov_thread_cmp, tc)
338 {
339 	thread_test(KCOV_MODE_TRACE_CMP);
340 }
341 
342 struct multi_thread_data {
343 	char *buf;
344 	int fd;
345 	u_int mode;
346 	int thread;
347 };
348 
349 static void *
350 multi_thread_test_helper(void *ptr)
351 {
352 	struct multi_thread_data *data = ptr;
353 
354 	ATF_REQUIRE_MSG(ioctl(data->fd, KIOENABLE, data->mode) == 0,
355 	    "Unable to enable kcov %s in thread %d",
356 	    data->mode < nitems(modes) ? modes[data->mode] : "unknown mode",
357 	    data->thread);
358 
359 	atomic_store_64(&data->buf[0], 0);
360 	sleep(0);
361 	ATF_REQUIRE_MSG(atomic_load_64(&data->buf[0]) != 0,
362 	    "No records found in thread %d", data->thread);
363 
364 	return (NULL);
365 }
366 
367 ATF_TC_WITHOUT_HEAD(kcov_enable_multi_thread);
368 ATF_TC_BODY(kcov_enable_multi_thread, t)
369 {
370 	struct multi_thread_data data;
371 	pthread_t thread;
372 
373 	data.buf = common_head(&data.fd);
374 
375 	/* Run the thread to completion */
376 	data.thread = 1;
377 	data.mode = KCOV_MODE_TRACE_PC;
378 	pthread_create(&thread, NULL, multi_thread_test_helper, &data);
379 	pthread_join(thread, NULL);
380 
381 	/* Run it again to check enable works on the same fd */
382 	data.thread = 2;
383 	data.mode = KCOV_MODE_TRACE_CMP;
384 	pthread_create(&thread, NULL, multi_thread_test_helper, &data);
385 	pthread_join(thread, NULL);
386 
387 	common_tail(data.fd, data.buf);
388 }
389 
390 ATF_TP_ADD_TCS(tp)
391 {
392 
393 	ATF_TP_ADD_TC(tp, kcov_bufsize);
394 	ATF_TP_ADD_TC(tp, kcov_mmap);
395 	ATF_TP_ADD_TC(tp, kcov_mmap_no_munmap);
396 	ATF_TP_ADD_TC(tp, kcov_mmap_no_munmap_no_close);
397 	ATF_TP_ADD_TC(tp, kcov_enable);
398 	ATF_TP_ADD_TC(tp, kcov_enable_no_disable);
399 	ATF_TP_ADD_TC(tp, kcov_enable_no_disable_no_close);
400 	ATF_TP_ADD_TC(tp, kcov_mmap_enable_thread_close);
401 	ATF_TP_ADD_TC(tp, kcov_basic_pc);
402 	ATF_TP_ADD_TC(tp, kcov_basic_cmp);
403 	ATF_TP_ADD_TC(tp, kcov_thread_pc);
404 	ATF_TP_ADD_TC(tp, kcov_thread_cmp);
405 	ATF_TP_ADD_TC(tp, kcov_enable_multi_thread);
406 	return (atf_no_error());
407 }
408