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