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