xref: /freebsd/tests/sys/kern/kcov.c (revision 3c4ba5f55438f7afd4f4b0b56f88f2bb505fd6a6)
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 size_t page_size;
54 
55 static void
56 init_page_size(void)
57 {
58 	page_size = getpagesize();
59 }
60 
61 static int
62 open_kcov(void)
63 {
64 	int fd;
65 
66 	fd = open("/dev/kcov", O_RDWR);
67 	if (fd == -1)
68 		atf_tc_skip("Failed to open /dev/kcov");
69 
70 	return (fd);
71 }
72 
73 ATF_TC(kcov_bufsize);
74 ATF_TC_HEAD(kcov_bufsize, tc)
75 {
76 	init_page_size();
77 }
78 
79 ATF_TC_BODY(kcov_bufsize, tc)
80 {
81 	int fd;
82 
83 	fd = open_kcov();
84 
85 	ATF_CHECK(ioctl(fd, KIOSETBUFSIZE, 0) == -1);
86 	ATF_CHECK(ioctl(fd, KIOSETBUFSIZE, 1) == -1);
87 	ATF_CHECK(ioctl(fd, KIOSETBUFSIZE, 2) == 0);
88 	ATF_CHECK(ioctl(fd, KIOSETBUFSIZE, 2) == -1);
89 
90 	close(fd);
91 }
92 
93 ATF_TC(kcov_mmap);
94 ATF_TC_HEAD(kcov_mmap, tc)
95 {
96 	init_page_size();
97 }
98 
99 ATF_TC_BODY(kcov_mmap, tc)
100 {
101 	void *data1, *data2;
102 	int fd;
103 
104 	fd = open_kcov();
105 
106 	ATF_CHECK(mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED,
107 	    fd, 0) == MAP_FAILED);
108 
109 	ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE,
110 	    2 * page_size / KCOV_ENTRY_SIZE) == 0);
111 
112 	ATF_CHECK(mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED,
113 	    fd, 0) == MAP_FAILED);
114 	ATF_CHECK(mmap(NULL, 3 * page_size, PROT_READ | PROT_WRITE, MAP_SHARED,
115 	    fd, 0) == MAP_FAILED);
116 	ATF_REQUIRE((data1 = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
117 	    MAP_SHARED, fd, 0)) != MAP_FAILED);
118 	ATF_REQUIRE((data2 = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
119 	    MAP_SHARED, fd, 0)) != MAP_FAILED);
120 
121 	*(uint64_t *)data1 = 0x123456789abcdeful;
122 	ATF_REQUIRE(*(uint64_t *)data2 == 0x123456789abcdefull);
123 	*(uint64_t *)data2 = 0xfedcba9876543210ul;
124 	ATF_REQUIRE(*(uint64_t *)data1 == 0xfedcba9876543210ull);
125 
126 	munmap(data1, 2 * page_size);
127 	munmap(data2, 2 * page_size);
128 
129 	close(fd);
130 }
131 
132 /* This shouldn't panic */
133 ATF_TC(kcov_mmap_no_munmap);
134 ATF_TC_HEAD(kcov_mmap_no_munmap, tc)
135 {
136 	init_page_size();
137 }
138 
139 ATF_TC_BODY(kcov_mmap_no_munmap, tc)
140 {
141 	int fd;
142 
143 	fd = open_kcov();
144 
145 	ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE, page_size / KCOV_ENTRY_SIZE) == 0);
146 
147 	ATF_CHECK(mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED,
148 	    fd, 0) != MAP_FAILED);
149 
150 	close(fd);
151 }
152 
153 ATF_TC(kcov_mmap_no_munmap_no_close);
154 ATF_TC_HEAD(kcov_mmap_no_munmap_no_close, tc)
155 {
156 	init_page_size();
157 }
158 
159 ATF_TC_BODY(kcov_mmap_no_munmap_no_close, tc)
160 {
161 	int fd;
162 
163 	fd = open_kcov();
164 
165 	ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE, page_size / KCOV_ENTRY_SIZE) == 0);
166 
167 	ATF_CHECK(mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED,
168 	    fd, 0) != MAP_FAILED);
169 }
170 
171 static sem_t sem1, sem2;
172 
173 static void *
174 kcov_mmap_enable_thread(void *data)
175 {
176 	int fd;
177 
178 	fd = open_kcov();
179 	*(int *)data = fd;
180 
181 	ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE, page_size / KCOV_ENTRY_SIZE) == 0);
182 	ATF_CHECK(mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED,
183 	    fd, 0) != MAP_FAILED);
184 	ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_PC) == 0);
185 
186 	sem_post(&sem1);
187 	sem_wait(&sem2);
188 
189 	return (NULL);
190 }
191 
192 ATF_TC(kcov_mmap_enable_thread_close);
193 ATF_TC_HEAD(kcov_mmap_enable_thread_close, tc)
194 {
195 	init_page_size();
196 }
197 
198 ATF_TC_BODY(kcov_mmap_enable_thread_close, tc)
199 {
200 	pthread_t thread;
201 	int fd;
202 
203 	sem_init(&sem1, 0, 0);
204 	sem_init(&sem2, 0, 0);
205 	pthread_create(&thread, NULL,
206 	    kcov_mmap_enable_thread, &fd);
207 	sem_wait(&sem1);
208 	close(fd);
209 	sem_post(&sem2);
210 	pthread_join(thread, NULL);
211 }
212 
213 ATF_TC(kcov_enable);
214 ATF_TC_HEAD(kcov_enable, tc)
215 {
216 	init_page_size();
217 }
218 
219 ATF_TC_BODY(kcov_enable, tc)
220 {
221 	int fd;
222 
223 	fd = open_kcov();
224 
225 	ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_PC) == -1);
226 
227 	ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE, page_size / KCOV_ENTRY_SIZE) == 0);
228 
229 	/* We need to enable before disable */
230 	ATF_CHECK(ioctl(fd, KIODISABLE, 0) == -1);
231 
232 	/* Check enabling works only with a valid trace method */
233 	ATF_CHECK(ioctl(fd, KIOENABLE, -1) == -1);
234 	ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_PC) == 0);
235 	ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_PC) == -1);
236 	ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_CMP) == -1);
237 
238 	/* Disable should only be called once */
239 	ATF_CHECK(ioctl(fd, KIODISABLE, 0) == 0);
240 	ATF_CHECK(ioctl(fd, KIODISABLE, 0) == -1);
241 
242 	/* Re-enabling should also work */
243 	ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_CMP) == 0);
244 	ATF_CHECK(ioctl(fd, KIODISABLE, 0) == 0);
245 
246 	close(fd);
247 }
248 
249 ATF_TC(kcov_enable_no_disable);
250 ATF_TC_HEAD(kcov_enable_no_disable, tc)
251 {
252 	init_page_size();
253 }
254 
255 ATF_TC_BODY(kcov_enable_no_disable, tc)
256 {
257 	int fd;
258 
259 	fd = open_kcov();
260 	ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE, page_size / KCOV_ENTRY_SIZE) == 0);
261 	ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_PC) == 0);
262 	close(fd);
263 }
264 
265 ATF_TC(kcov_enable_no_disable_no_close);
266 ATF_TC_HEAD(kcov_enable_no_disable_no_close, tc)
267 {
268 	init_page_size();
269 }
270 
271 ATF_TC_BODY(kcov_enable_no_disable_no_close, tc)
272 {
273 	int fd;
274 
275 	fd = open_kcov();
276 	ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE, page_size / KCOV_ENTRY_SIZE) == 0);
277 	ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_PC) == 0);
278 }
279 
280 static void *
281 common_head(int *fdp)
282 {
283 	void *data;
284 	int fd;
285 
286 	fd = open_kcov();
287 
288 	ATF_REQUIRE_MSG(ioctl(fd, KIOSETBUFSIZE,
289 	    page_size / KCOV_ENTRY_SIZE) == 0,
290 	    "Unable to set the kcov buffer size");
291 
292 	data = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
293 	ATF_REQUIRE_MSG(data != MAP_FAILED, "Unable to mmap the kcov buffer");
294 
295 	*fdp = fd;
296 	return (data);
297 }
298 
299 static void
300 common_tail(int fd, void *data)
301 {
302 
303 	ATF_REQUIRE_MSG(munmap(data, page_size) == 0,
304 	    "Unable to unmap the kcov buffer");
305 
306 	close(fd);
307 }
308 
309 static void
310 basic_test(u_int mode)
311 {
312 	uint64_t *buf;
313 	int fd;
314 
315 	buf = common_head(&fd);
316 	ATF_REQUIRE_MSG(ioctl(fd, KIOENABLE, mode) == 0,
317 	    "Unable to enable kcov %s",
318 	    mode < nitems(modes) ? modes[mode] : "unknown mode");
319 
320 	atomic_store_64(&buf[0], 0);
321 
322 	sleep(0);
323 	ATF_REQUIRE_MSG(atomic_load_64(&buf[0]) != 0, "No records found");
324 
325 	ATF_REQUIRE_MSG(ioctl(fd, KIODISABLE, 0) == 0,
326 	    "Unable to disable kcov");
327 
328 	common_tail(fd, buf);
329 }
330 
331 ATF_TC(kcov_basic_pc);
332 ATF_TC_HEAD(kcov_basic_pc, tc)
333 {
334 	init_page_size();
335 }
336 
337 ATF_TC_BODY(kcov_basic_pc, tc)
338 {
339 	basic_test(KCOV_MODE_TRACE_PC);
340 }
341 
342 ATF_TC(kcov_basic_cmp);
343 ATF_TC_HEAD(kcov_basic_cmp, tc)
344 {
345 	init_page_size();
346 }
347 
348 ATF_TC_BODY(kcov_basic_cmp, tc)
349 {
350 	basic_test(KCOV_MODE_TRACE_CMP);
351 }
352 
353 static void *
354 thread_test_helper(void *ptr)
355 {
356 	uint64_t *buf = ptr;
357 
358 	atomic_store_64(&buf[0], 0);
359 	sleep(0);
360 	ATF_REQUIRE_MSG(atomic_load_64(&buf[0]) == 0,
361 	    "Records changed in blocked thread");
362 
363 	return (NULL);
364 }
365 
366 static void
367 thread_test(u_int mode)
368 {
369 	pthread_t thread;
370 	uint64_t *buf;
371 	int fd;
372 
373 	buf = common_head(&fd);
374 
375 	ATF_REQUIRE_MSG(ioctl(fd, KIOENABLE, mode) == 0,
376 	    "Unable to enable kcov %s",
377 	    mode < nitems(modes) ? modes[mode] : "unknown mode");
378 
379 	pthread_create(&thread, NULL, thread_test_helper, buf);
380 	pthread_join(thread, NULL);
381 
382 	ATF_REQUIRE_MSG(ioctl(fd, KIODISABLE, 0) == 0,
383 	    "Unable to disable kcov");
384 
385 	common_tail(fd, buf);
386 }
387 
388 ATF_TC(kcov_thread_pc);
389 ATF_TC_HEAD(kcov_thread_pc, tc)
390 {
391 	init_page_size();
392 }
393 
394 ATF_TC_BODY(kcov_thread_pc, tc)
395 {
396 	thread_test(KCOV_MODE_TRACE_PC);
397 }
398 
399 ATF_TC(kcov_thread_cmp);
400 ATF_TC_HEAD(kcov_thread_cmp, tc)
401 {
402 	init_page_size();
403 }
404 
405 ATF_TC_BODY(kcov_thread_cmp, tc)
406 {
407 	thread_test(KCOV_MODE_TRACE_CMP);
408 }
409 
410 struct multi_thread_data {
411 	uint64_t *buf;
412 	int fd;
413 	u_int mode;
414 	int thread;
415 };
416 
417 static void *
418 multi_thread_test_helper(void *ptr)
419 {
420 	struct multi_thread_data *data = ptr;
421 
422 	ATF_REQUIRE_MSG(ioctl(data->fd, KIOENABLE, data->mode) == 0,
423 	    "Unable to enable kcov %s in thread %d",
424 	    data->mode < nitems(modes) ? modes[data->mode] : "unknown mode",
425 	    data->thread);
426 
427 	atomic_store_64(&data->buf[0], 0);
428 	sleep(0);
429 	ATF_REQUIRE_MSG(atomic_load_64(&data->buf[0]) != 0,
430 	    "No records found in thread %d", data->thread);
431 
432 	return (NULL);
433 }
434 
435 ATF_TC(kcov_enable_multi_thread);
436 ATF_TC_HEAD(kcov_enable_multi_thread, t)
437 {
438 	init_page_size();
439 }
440 
441 ATF_TC_BODY(kcov_enable_multi_thread, t)
442 {
443 	struct multi_thread_data data;
444 	pthread_t thread;
445 
446 	data.buf = common_head(&data.fd);
447 
448 	/* Run the thread to completion */
449 	data.thread = 1;
450 	data.mode = KCOV_MODE_TRACE_PC;
451 	pthread_create(&thread, NULL, multi_thread_test_helper, &data);
452 	pthread_join(thread, NULL);
453 
454 	/* Run it again to check enable works on the same fd */
455 	data.thread = 2;
456 	data.mode = KCOV_MODE_TRACE_CMP;
457 	pthread_create(&thread, NULL, multi_thread_test_helper, &data);
458 	pthread_join(thread, NULL);
459 
460 	common_tail(data.fd, data.buf);
461 }
462 
463 ATF_TP_ADD_TCS(tp)
464 {
465 
466 	ATF_TP_ADD_TC(tp, kcov_bufsize);
467 	ATF_TP_ADD_TC(tp, kcov_mmap);
468 	ATF_TP_ADD_TC(tp, kcov_mmap_no_munmap);
469 	ATF_TP_ADD_TC(tp, kcov_mmap_no_munmap_no_close);
470 	ATF_TP_ADD_TC(tp, kcov_enable);
471 	ATF_TP_ADD_TC(tp, kcov_enable_no_disable);
472 	ATF_TP_ADD_TC(tp, kcov_enable_no_disable_no_close);
473 	ATF_TP_ADD_TC(tp, kcov_mmap_enable_thread_close);
474 	ATF_TP_ADD_TC(tp, kcov_basic_pc);
475 	ATF_TP_ADD_TC(tp, kcov_basic_cmp);
476 	ATF_TP_ADD_TC(tp, kcov_thread_pc);
477 	ATF_TP_ADD_TC(tp, kcov_thread_cmp);
478 	ATF_TP_ADD_TC(tp, kcov_enable_multi_thread);
479 	return (atf_no_error());
480 }
481