xref: /netbsd-src/tests/modules/t_kcov.c (revision 181254a7b1bdde6873432bffef2d2decc4b5c22f)
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 #include <sys/cdefs.h>
32 
33 #include <sys/param.h>
34 #include <sys/ioctl.h>
35 #include <sys/kcov.h>
36 #include <sys/mman.h>
37 
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <pthread.h>
41 #include <semaphore.h>
42 #include <unistd.h>
43 
44 #include <atf-c.h>
45 
46 #define PAGE_SIZE sysconf(_SC_PAGESIZE)
47 
48 static int
49 open_kcov(void)
50 {
51 	int fd;
52 
53 	fd = open("/dev/kcov", O_RDWR);
54 	if (fd == -1)
55 		atf_tc_skip("Failed to open /dev/kcov");
56 
57 	return fd;
58 }
59 
60 static int
61 pick_unassigned_fd(int greater_than_fd)
62 {
63 	int fd2;
64 
65 	fd2 = greater_than_fd;
66 	do {
67 		++fd2;
68 	} while (fcntl(fd2, F_GETFL) != -1 || errno != EBADF);
69 
70 	return fd2;
71 }
72 
73 ATF_TC_WITHOUT_HEAD(kcov_dup2);
74 ATF_TC_BODY(kcov_dup2, tc)
75 {
76 	int fd1, fd2;
77 	fd1 = open_kcov();
78 
79 	fd2 = pick_unassigned_fd(fd1);
80 
81 	/* Test the dup2(2) trick used by syzkaller */
82 	ATF_REQUIRE_EQ(dup2(fd1, fd2), fd2);
83 
84 	close(fd1);
85 	close(fd2);
86 }
87 
88 ATF_TC_WITHOUT_HEAD(kcov_multiopen);
89 ATF_TC_BODY(kcov_multiopen, tc)
90 {
91 	int fd1, fd2;
92 	fd1 = open_kcov();
93 
94 	fd2 = open("/dev/kcov", O_RDWR);
95 	ATF_REQUIRE(fd2 != -1);
96 
97 	close(fd1);
98 	close(fd2);
99 }
100 
101 ATF_TC_WITHOUT_HEAD(kcov_open_close_open);
102 ATF_TC_BODY(kcov_open_close_open, tc)
103 {
104 	int fd;
105 
106 	fd = open_kcov();
107 	close(fd);
108 	fd = open("/dev/kcov", O_RDWR);
109 	ATF_REQUIRE(fd != -1);
110 
111 	close(fd);
112 }
113 
114 ATF_TC_WITHOUT_HEAD(kcov_bufsize);
115 ATF_TC_BODY(kcov_bufsize, tc)
116 {
117 	int fd;
118 	uint64_t size;
119 	fd = open_kcov();
120 
121 	size = 0;
122 	ATF_CHECK(ioctl(fd, KCOV_IOC_SETBUFSIZE, &size) == -1);
123 	size = 2;
124 	ATF_CHECK(ioctl(fd, KCOV_IOC_SETBUFSIZE, &size) == 0);
125 
126 	close(fd);
127 }
128 
129 ATF_TC_WITHOUT_HEAD(kcov_mmap);
130 ATF_TC_BODY(kcov_mmap, tc)
131 {
132 	void *data;
133 	int fd;
134 	uint64_t size = 2 * PAGE_SIZE / KCOV_ENTRY_SIZE;
135 
136 	fd = open_kcov();
137 
138 	ATF_CHECK(mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
139 	    fd, 0) == MAP_FAILED);
140 
141 	ATF_REQUIRE(ioctl(fd, KCOV_IOC_SETBUFSIZE, &size) == 0);
142 
143 	ATF_REQUIRE((data = mmap(NULL, 2 * PAGE_SIZE, PROT_READ | PROT_WRITE,
144 	    MAP_SHARED, fd, 0)) != MAP_FAILED);
145 
146 	munmap(data, 2 * PAGE_SIZE);
147 
148 	close(fd);
149 }
150 
151 /* This shouldn't panic */
152 ATF_TC_WITHOUT_HEAD(kcov_mmap_no_munmap);
153 ATF_TC_BODY(kcov_mmap_no_munmap, tc)
154 {
155 	int fd;
156 	uint64_t size = PAGE_SIZE / KCOV_ENTRY_SIZE;
157 
158 	fd = open_kcov();
159 
160 	ATF_REQUIRE(ioctl(fd, KCOV_IOC_SETBUFSIZE, &size) ==0);
161 
162 	ATF_CHECK(mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
163 	    fd, 0) != MAP_FAILED);
164 
165 	close(fd);
166 }
167 
168 ATF_TC_WITHOUT_HEAD(kcov_mmap_no_munmap_no_close);
169 ATF_TC_BODY(kcov_mmap_no_munmap_no_close, tc)
170 {
171 	int fd;
172 	uint64_t size = PAGE_SIZE / KCOV_ENTRY_SIZE;
173 
174 	fd = open_kcov();
175 
176 	ATF_REQUIRE(ioctl(fd, KCOV_IOC_SETBUFSIZE, &size) ==0);
177 
178 	ATF_CHECK(mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
179 	    fd, 0) != MAP_FAILED);
180 }
181 
182 static sem_t sem1, sem2;
183 
184 static void *
185 kcov_mmap_enable_thread(void *data)
186 {
187 	int fd;
188 	uint64_t size = PAGE_SIZE / KCOV_ENTRY_SIZE;
189 	int mode;
190 
191 	fd = open_kcov();
192 	*(int *)data = fd;
193 
194 	ATF_REQUIRE(ioctl(fd, KCOV_IOC_SETBUFSIZE, &size) ==0);
195 	ATF_CHECK(mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
196 	    fd, 0) != MAP_FAILED);
197 	mode = KCOV_MODE_NONE;
198 	ATF_CHECK(ioctl(fd, KCOV_IOC_ENABLE, &mode) == 0);
199 
200 	sem_post(&sem1);
201 	sem_wait(&sem2);
202 
203 	return NULL;
204 }
205 
206 ATF_TC_WITHOUT_HEAD(kcov_mmap_enable_thread_close);
207 ATF_TC_BODY(kcov_mmap_enable_thread_close, tc)
208 {
209 	pthread_t thread;
210 	int fd;
211 
212 	sem_init(&sem1, 0, 0);
213 	sem_init(&sem2, 0, 0);
214 	pthread_create(&thread, NULL,
215 	    kcov_mmap_enable_thread, &fd);
216 	sem_wait(&sem1);
217 	close(fd);
218 	sem_post(&sem2);
219 	pthread_join(thread, NULL);
220 }
221 
222 ATF_TC_WITHOUT_HEAD(kcov_enable);
223 ATF_TC_BODY(kcov_enable, tc)
224 {
225 	int fd;
226 	uint64_t size = PAGE_SIZE / KCOV_ENTRY_SIZE;
227 	int mode;
228 
229 	fd = open_kcov();
230 
231 	mode = KCOV_MODE_NONE;
232 	ATF_CHECK(ioctl(fd, KCOV_IOC_ENABLE, &mode) == -1);
233 
234 	ATF_REQUIRE(ioctl(fd, KCOV_IOC_SETBUFSIZE, &size) ==0);
235 
236 	/* We need to enable before disable */
237 	ATF_CHECK(ioctl(fd, KCOV_IOC_DISABLE) == -1);
238 
239 	/* Check enabling works only with a valid trace method */
240 	ATF_CHECK(ioctl(fd, KCOV_IOC_ENABLE, &mode) == 0);
241 	ATF_CHECK(ioctl(fd, KCOV_IOC_ENABLE, &mode) == -1);
242 
243 	/* Disable should only be called once */
244 	ATF_CHECK(ioctl(fd, KCOV_IOC_DISABLE) == 0);
245 	ATF_CHECK(ioctl(fd, KCOV_IOC_DISABLE) == -1);
246 
247 	/* Re-enabling and changing mode should also work */
248 	mode = KCOV_MODE_NONE;
249 	ATF_CHECK(ioctl(fd, KCOV_IOC_ENABLE, &mode) == 0);
250 	ATF_CHECK(ioctl(fd, KCOV_IOC_DISABLE) == 0);
251 	mode = KCOV_MODE_TRACE_PC;
252 	ATF_CHECK(ioctl(fd, KCOV_IOC_ENABLE, &mode) == 0);
253 	ATF_CHECK(ioctl(fd, KCOV_IOC_DISABLE) == 0);
254 	mode = KCOV_MODE_TRACE_CMP;
255 	ATF_CHECK(ioctl(fd, KCOV_IOC_ENABLE, &mode) == 0);
256 	ATF_CHECK(ioctl(fd, KCOV_IOC_DISABLE) == 0);
257 
258 	close(fd);
259 }
260 
261 ATF_TC_WITHOUT_HEAD(kcov_enable_no_disable);
262 ATF_TC_BODY(kcov_enable_no_disable, tc)
263 {
264 	int fd;
265 	uint64_t size = PAGE_SIZE / KCOV_ENTRY_SIZE;
266 	int mode;
267 
268 	fd = open_kcov();
269 	ATF_REQUIRE(ioctl(fd, KCOV_IOC_SETBUFSIZE, &size) ==0);
270 	mode = KCOV_MODE_NONE;
271 	ATF_CHECK(ioctl(fd, KCOV_IOC_ENABLE, &mode) == 0);
272 	close(fd);
273 }
274 
275 ATF_TC_WITHOUT_HEAD(kcov_enable_no_disable_no_close);
276 ATF_TC_BODY(kcov_enable_no_disable_no_close, tc)
277 {
278 	int fd;
279 	uint64_t size = PAGE_SIZE / KCOV_ENTRY_SIZE;
280 	int mode;
281 
282 	fd = open_kcov();
283 	ATF_REQUIRE(ioctl(fd, KCOV_IOC_SETBUFSIZE, &size) ==0);
284 	mode = KCOV_MODE_NONE;
285 	ATF_CHECK(ioctl(fd, KCOV_IOC_ENABLE, &mode) == 0);
286 }
287 
288 static void *
289 common_head_raw(bool fd_dup, int *fdp)
290 {
291 	void *data;
292 	int fd, fd2;
293 	uint64_t size = PAGE_SIZE / KCOV_ENTRY_SIZE;
294 
295 	fd = open_kcov();
296 
297 	/* Test the dup2(2) trick used by syzkaller */
298 	if (fd_dup) {
299 		fd2 = pick_unassigned_fd(fd);
300 		ATF_REQUIRE_EQ(dup2(fd, fd2), fd2);
301 		close(fd);
302 		fd = fd2;
303 	}
304 
305 	ATF_REQUIRE_MSG(ioctl(fd, KCOV_IOC_SETBUFSIZE, &size) == 0,
306 	    "Unable to set the kcov buffer size");
307 
308 	data = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
309 	ATF_REQUIRE_MSG(data != MAP_FAILED, "Unable to mmap the kcov buffer");
310 
311 	*fdp = fd;
312 	return data;
313 }
314 
315 static void *
316 common_head(int *fdp)
317 {
318 
319 	return common_head_raw(false, fdp);
320 }
321 
322 static void
323 common_tail(int fd, kcov_int_t *data)
324 {
325 
326 	ATF_REQUIRE_MSG(munmap(__UNVOLATILE(data), PAGE_SIZE) == 0,
327 	    "Unable to unmap the kcov buffer");
328 
329 	close(fd);
330 }
331 
332 static void
333 kcov_basic(bool fd_dup, int mode)
334 {
335 	kcov_int_t *buf;
336 	int fd;
337 
338 	buf = common_head_raw(fd_dup, &fd);
339 	ATF_REQUIRE_MSG(ioctl(fd, KCOV_IOC_ENABLE, &mode) == 0,
340 	    "Unable to enable kcov ");
341 
342 	buf[0] = 0;
343 
344 	sleep(0); /* XXX: Is it enough for all trace types? */
345 	ATF_REQUIRE_MSG(buf[0] != 0, "No records found");
346 
347 	ATF_REQUIRE_MSG(ioctl(fd, KCOV_IOC_DISABLE) == 0,
348 	    "Unable to disable kcov");
349 
350 	common_tail(fd, buf);
351 }
352 
353 ATF_TC_WITHOUT_HEAD(kcov_basic_pc);
354 ATF_TC_BODY(kcov_basic_pc, tc)
355 {
356 
357 	kcov_basic(false, KCOV_MODE_TRACE_PC);
358 }
359 
360 ATF_TC_WITHOUT_HEAD(kcov_basic_cmp);
361 ATF_TC_BODY(kcov_basic_cmp, tc)
362 {
363 
364 	atf_tc_skip("XXX: GCC8 needed");
365 
366 	kcov_basic(false, KCOV_MODE_TRACE_CMP);
367 }
368 
369 ATF_TC_WITHOUT_HEAD(kcov_basic_dup2_pc);
370 ATF_TC_BODY(kcov_basic_dup2_pc, tc)
371 {
372 
373 	kcov_basic(true, KCOV_MODE_TRACE_PC);
374 }
375 
376 ATF_TC_WITHOUT_HEAD(kcov_basic_dup2_cmp);
377 ATF_TC_BODY(kcov_basic_dup2_cmp, tc)
378 {
379 
380 	atf_tc_skip("XXX: GCC8 needed");
381 
382 	kcov_basic(true, KCOV_MODE_TRACE_CMP);
383 }
384 
385 ATF_TC_WITHOUT_HEAD(kcov_multienable_on_the_same_thread);
386 ATF_TC_BODY(kcov_multienable_on_the_same_thread, tc)
387 {
388 	kcov_int_t *buf1, *buf2;
389 	int fd1, fd2;
390 	int mode;
391 
392 	buf1 = common_head(&fd1);
393 	buf2 = common_head(&fd2);
394 	mode = KCOV_MODE_NONE;
395 	ATF_REQUIRE_MSG(ioctl(fd1, KCOV_IOC_ENABLE, &mode) == 0,
396 	    "Unable to enable kcov");
397 	ATF_REQUIRE_ERRNO(EBUSY, ioctl(fd2, KCOV_IOC_ENABLE, &mode) != 0);
398 
399 	ATF_REQUIRE_MSG(ioctl(fd1, KCOV_IOC_DISABLE) == 0,
400 	    "Unable to disable kcov");
401 
402 	common_tail(fd1, buf1);
403 	common_tail(fd2, buf2);
404 }
405 
406 static void *
407 thread_buffer_access_test_helper(void *ptr)
408 {
409 	kcov_int_t *buf = ptr;
410 
411 	/* Test mapped buffer access from a custom thread */
412 	buf[0] = buf[0];
413 
414 	return NULL;
415 }
416 
417 ATF_TC_WITHOUT_HEAD(kcov_buffer_access_from_custom_thread);
418 ATF_TC_BODY(kcov_buffer_access_from_custom_thread, tc)
419 {
420 	pthread_t thread;
421 	kcov_int_t *buf;
422 	int fd;
423 	int mode;
424 
425 	buf = common_head(&fd);
426 
427 	mode = KCOV_MODE_TRACE_PC;
428 	ATF_REQUIRE_MSG(ioctl(fd, KCOV_IOC_ENABLE, &mode) == 0,
429 	    "Unable to enable kcov ");
430 
431 	pthread_create(&thread, NULL, thread_buffer_access_test_helper,
432 	    __UNVOLATILE(buf));
433 	pthread_join(thread, NULL);
434 
435 	ATF_REQUIRE_MSG(ioctl(fd, KCOV_IOC_DISABLE) == 0,
436 	    "Unable to disable kcov");
437 
438 	common_tail(fd, buf);
439 }
440 
441 static void *
442 thread_test_helper(void *ptr)
443 {
444 	volatile int i;
445 
446 	/* It does not matter what operation is in action. */
447 	for (i = 0; i < 1000; i++) {
448 		if (getpid() == 0)
449 			break;
450 	}
451 
452 	return NULL;
453 }
454 
455 ATF_TC_WITHOUT_HEAD(kcov_thread);
456 ATF_TC_BODY(kcov_thread, tc)
457 {
458 	pthread_t thread;
459 	kcov_int_t *buf;
460 	int fd;
461 	int mode;
462 	volatile int i;
463 
464 	buf = common_head(&fd);
465 
466 	mode = KCOV_MODE_TRACE_PC;
467 	ATF_REQUIRE_MSG(ioctl(fd, KCOV_IOC_ENABLE, &mode) == 0,
468 	    "Unable to enable kcov ");
469 
470 	/* The thread does something, does not matter what exactly. */
471 	pthread_create(&thread, NULL, thread_test_helper, __UNVOLATILE(buf));
472 
473 	buf[0] = 0;
474 	for (i = 0; i < 10000; i++)
475 		continue;
476 	ATF_REQUIRE_EQ_MSG(buf[0], 0,
477 	    "Records changed in blocked thread");
478 
479 	pthread_join(thread, NULL);
480 
481 	ATF_REQUIRE_EQ_MSG(ioctl(fd, KCOV_IOC_DISABLE), 0,
482 	    "Unable to disable kcov");
483 
484 	common_tail(fd, buf);
485 }
486 
487 static void *
488 multiple_threads_helper(void *ptr __unused)
489 {
490 	kcov_int_t *buf;
491 	int fd;
492 	int mode;
493 
494 	buf = common_head(&fd);
495 	mode = KCOV_MODE_TRACE_PC;
496 	ATF_REQUIRE_MSG(ioctl(fd, KCOV_IOC_ENABLE, &mode) == 0,
497 	    "Unable to enable kcov ");
498 
499 	buf[0] = 0;
500 
501 	sleep(0);
502 	ATF_REQUIRE_MSG(buf[0] != 0, "No records found");
503 
504 	ATF_REQUIRE_MSG(ioctl(fd, KCOV_IOC_DISABLE) == 0,
505 	    "Unable to disable kcov");
506 
507 	common_tail(fd, buf);
508 
509 	return NULL;
510 }
511 
512 static void
513 kcov_multiple_threads(size_t N)
514 {
515 	pthread_t thread[32];
516 	size_t i;
517 	int fd;
518 
519 	/*
520 	 * Check if /dev/kcov is available, if not bail out.
521 	 * Verifying it on a per-thread basis is flaky.
522 	 */
523 	fd = open_kcov();
524 	ATF_REQUIRE(close(fd) == 0);
525 
526 	ATF_REQUIRE(__arraycount(thread) >= N);
527 
528 	for (i = 0; i < N; i++)
529 		pthread_create(&thread[i], NULL, multiple_threads_helper, NULL);
530 
531 	for (i = 0; i < N; i++)
532 		pthread_join(thread[i], NULL);
533 }
534 
535 #define KCOV_MULTIPLE_THREADS(n)		\
536 ATF_TC_WITHOUT_HEAD(kcov_multiple_threads##n);	\
537 ATF_TC_BODY(kcov_multiple_threads##n, tc)	\
538 {						\
539 						\
540 	kcov_multiple_threads(n);		\
541 }
542 
543 KCOV_MULTIPLE_THREADS(2)
544 KCOV_MULTIPLE_THREADS(4)
545 KCOV_MULTIPLE_THREADS(8)
546 KCOV_MULTIPLE_THREADS(16)
547 KCOV_MULTIPLE_THREADS(32)
548 
549 ATF_TP_ADD_TCS(tp)
550 {
551 
552 	ATF_TP_ADD_TC(tp, kcov_dup2);
553 	ATF_TP_ADD_TC(tp, kcov_multiopen);
554 	ATF_TP_ADD_TC(tp, kcov_open_close_open);
555 	ATF_TP_ADD_TC(tp, kcov_bufsize);
556 	ATF_TP_ADD_TC(tp, kcov_mmap);
557 	ATF_TP_ADD_TC(tp, kcov_mmap_no_munmap);
558 	ATF_TP_ADD_TC(tp, kcov_mmap_no_munmap_no_close);
559 	ATF_TP_ADD_TC(tp, kcov_enable);
560 	ATF_TP_ADD_TC(tp, kcov_enable_no_disable);
561 	ATF_TP_ADD_TC(tp, kcov_enable_no_disable_no_close);
562 	ATF_TP_ADD_TC(tp, kcov_mmap_enable_thread_close);
563 	ATF_TP_ADD_TC(tp, kcov_basic_pc);
564 	ATF_TP_ADD_TC(tp, kcov_basic_cmp);
565 	ATF_TP_ADD_TC(tp, kcov_basic_dup2_pc);
566 	ATF_TP_ADD_TC(tp, kcov_basic_dup2_cmp);
567 	ATF_TP_ADD_TC(tp, kcov_multienable_on_the_same_thread);
568 	ATF_TP_ADD_TC(tp, kcov_buffer_access_from_custom_thread);
569 	ATF_TP_ADD_TC(tp, kcov_thread);
570 	ATF_TP_ADD_TC(tp, kcov_multiple_threads2);
571 	ATF_TP_ADD_TC(tp, kcov_multiple_threads4);
572 	ATF_TP_ADD_TC(tp, kcov_multiple_threads8);
573 	ATF_TP_ADD_TC(tp, kcov_multiple_threads16);
574 	ATF_TP_ADD_TC(tp, kcov_multiple_threads32);
575 	return atf_no_error();
576 }
577