1 /* $OpenBSD: kcov.c,v 1.18 2024/08/23 12:56:26 anton Exp $ */
2
3 /*
4 * Copyright (c) 2018 Anton Lindqvist <anton@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/types.h>
20 #include <sys/event.h>
21 #include <sys/ioctl.h>
22 #include <sys/kcov.h>
23 #include <sys/mman.h>
24 #include <sys/socket.h>
25 #include <sys/un.h>
26 #include <sys/wait.h>
27
28 #include <err.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <pthread.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36
37 struct context {
38 int c_fd;
39 int c_mode;
40 unsigned long c_bufsize;
41 };
42
43 static int test_close(struct context *);
44 static int test_coverage(struct context *);
45 static int test_dying(struct context *);
46 static int test_exec(struct context *);
47 static int test_fdsend(struct context *);
48 static int test_fork(struct context *);
49 static int test_open(struct context *);
50 static int test_remote(struct context *);
51 static int test_remote_close(struct context *);
52 static int test_remote_interrupt(struct context *);
53 static int test_state(struct context *);
54
55 static int check_coverage(const unsigned long *, int, unsigned long, int);
56 static void do_syscall(void);
57 static void dump(const unsigned long *, int mode);
58 static void kcov_disable(int);
59 static void kcov_enable(int, int);
60 static int kcov_open(void);
61 static __dead void usage(void);
62
63 static const char *self;
64
65 int
main(int argc,char * argv[])66 main(int argc, char *argv[])
67 {
68 struct {
69 const char *name;
70 int (*fn)(struct context *);
71 int coverage; /* test must produce coverage */
72 } tests[] = {
73 { "close", test_close, 0 },
74 { "coverage", test_coverage, 1 },
75 { "dying", test_dying, -1 },
76 { "exec", test_exec, 1 },
77 { "fdsend", test_fdsend, -1 },
78 { "fork", test_fork, 1 },
79 { "open", test_open, 0 },
80 { "remote", test_remote, 1 },
81 { "remote-close", test_remote_close, 0 },
82 { "remote-interrupt", test_remote_interrupt, -1 },
83 { "state", test_state, 1 },
84 { NULL, NULL, 0 },
85 };
86 struct context ctx;
87 const char *errstr;
88 unsigned long *cover, frac;
89 int c, i;
90 int error = 0;
91 int prereq = 0;
92 int reexec = 0;
93 int verbose = 0;
94
95 self = argv[0];
96
97 memset(&ctx, 0, sizeof(ctx));
98 ctx.c_bufsize = 256 << 10;
99
100 while ((c = getopt(argc, argv, "b:Em:pv")) != -1)
101 switch (c) {
102 case 'b':
103 frac = strtonum(optarg, 1, 100, &errstr);
104 if (frac == 0)
105 errx(1, "buffer size fraction %s", errstr);
106 else if (frac > ctx.c_bufsize)
107 errx(1, "buffer size fraction too large");
108 ctx.c_bufsize /= frac;
109 break;
110 case 'E':
111 reexec = 1;
112 break;
113 case 'm':
114 if (strcmp(optarg, "pc") == 0)
115 ctx.c_mode = KCOV_MODE_TRACE_PC;
116 else if (strcmp(optarg, "cmp") == 0)
117 ctx.c_mode = KCOV_MODE_TRACE_CMP;
118 else
119 errx(1, "unknown mode %s", optarg);
120 break;
121 case 'p':
122 prereq = 1;
123 break;
124 case 'v':
125 verbose = 1;
126 break;
127 default:
128 usage();
129 }
130 argc -= optind;
131 argv += optind;
132
133 if (prereq) {
134 ctx.c_fd = kcov_open();
135 close(ctx.c_fd);
136 return 0;
137 }
138
139 if (reexec) {
140 do_syscall();
141 return 0;
142 }
143
144 if (ctx.c_mode == 0 || argc != 1)
145 usage();
146 for (i = 0; tests[i].name != NULL; i++)
147 if (strcmp(argv[0], tests[i].name) == 0)
148 break;
149 if (tests[i].name == NULL)
150 errx(1, "%s: no such test", argv[0]);
151
152 ctx.c_fd = kcov_open();
153 if (ioctl(ctx.c_fd, KIOSETBUFSIZE, &ctx.c_bufsize) == -1)
154 err(1, "ioctl: KIOSETBUFSIZE");
155 cover = mmap(NULL, ctx.c_bufsize * sizeof(unsigned long),
156 PROT_READ | PROT_WRITE, MAP_SHARED, ctx.c_fd, 0);
157 if (cover == MAP_FAILED)
158 err(1, "mmap");
159
160 *cover = 0;
161 error = tests[i].fn(&ctx);
162 if (verbose)
163 dump(cover, ctx.c_mode);
164 if (check_coverage(cover, ctx.c_mode, ctx.c_bufsize, tests[i].coverage))
165 error = 1;
166
167 if (munmap(cover, ctx.c_bufsize * sizeof(unsigned long)) == -1)
168 err(1, "munmap");
169 if (ctx.c_fd != -1) {
170 if (close(ctx.c_fd) == -1)
171 err(1, "close");
172 }
173
174 return error;
175 }
176
177 static __dead void
usage(void)178 usage(void)
179 {
180 fprintf(stderr, "usage: kcov [-Epv] [-b fraction] -m mode test\n");
181 exit(1);
182 }
183
184 static void
do_syscall(void)185 do_syscall(void)
186 {
187 getpid();
188 }
189
190 static int
check_coverage(const unsigned long * cover,int mode,unsigned long maxsize,int nonzero)191 check_coverage(const unsigned long *cover, int mode, unsigned long maxsize,
192 int nonzero)
193 {
194 unsigned long arg1, arg2, exp, i, pc, type;
195 int error = 0;
196
197 if (nonzero == -1) {
198 return 0;
199 } else if (nonzero && cover[0] == 0) {
200 warnx("coverage empty (count=0)");
201 return 1;
202 } else if (!nonzero && cover[0] != 0) {
203 warnx("coverage not empty (count=%lu)", *cover);
204 return 1;
205 } else if (cover[0] >= maxsize) {
206 warnx("coverage overflow (count=%lu, max=%lu)",
207 *cover, maxsize);
208 return 1;
209 }
210
211 if (mode == KCOV_MODE_TRACE_CMP) {
212 if (*cover * 4 >= maxsize) {
213 warnx("coverage cmp overflow (count=%lu, max=%lu)",
214 *cover * 4, maxsize);
215 return 1;
216 }
217
218 for (i = 0; i < cover[0]; i++) {
219 type = cover[i * 4 + 1];
220 arg1 = cover[i * 4 + 2];
221 arg2 = cover[i * 4 + 3];
222 pc = cover[i * 4 + 4];
223
224 exp = type >> 1;
225 if (exp <= 3)
226 continue;
227
228 warnx("coverage cmp invalid size (i=%lu, exp=%lx, "
229 "const=%ld, arg1=%lu, arg2=%lu, pc=%p)\n",
230 i, exp, type & 0x1, arg1, arg2, (void *)pc);
231 error = 1;
232 }
233 }
234
235 return error;
236 }
237
238 static void
dump(const unsigned long * cover,int mode)239 dump(const unsigned long *cover, int mode)
240 {
241 unsigned long i;
242 int stride = 1;
243
244 if (mode == KCOV_MODE_TRACE_CMP)
245 stride = 4;
246
247 for (i = 0; i < cover[0]; i++)
248 printf("%p\n", (void *)cover[i * stride + stride]);
249 }
250
251 static int
kcov_open(void)252 kcov_open(void)
253 {
254 int fd;
255
256 fd = open("/dev/kcov", O_RDWR);
257 if (fd == -1)
258 err(1, "open: /dev/kcov");
259 return fd;
260 }
261
262 static void
kcov_enable(int fd,int mode)263 kcov_enable(int fd, int mode)
264 {
265 if (ioctl(fd, KIOENABLE, &mode) == -1)
266 err(1, "ioctl: KIOENABLE");
267 }
268
269 static void
kcov_disable(int fd)270 kcov_disable(int fd)
271 {
272 if (ioctl(fd, KIODISABLE) == -1)
273 err(1, "ioctl: KIODISABLE");
274 }
275
276 /*
277 * Close before mmap.
278 */
279 static int
test_close(struct context * ctx)280 test_close(struct context *ctx)
281 {
282 int fd;
283
284 fd = kcov_open();
285 close(fd);
286 return 0;
287 }
288
289 /*
290 * Coverage of current thread.
291 */
292 static int
test_coverage(struct context * ctx)293 test_coverage(struct context *ctx)
294 {
295 kcov_enable(ctx->c_fd, ctx->c_mode);
296 do_syscall();
297 kcov_disable(ctx->c_fd);
298 return 0;
299 }
300
301 static void *
closer(void * arg)302 closer(void *arg)
303 {
304 struct context *ctx = arg;
305
306 close(ctx->c_fd);
307 return NULL;
308 }
309
310 /*
311 * Close kcov descriptor in another thread during tracing.
312 */
313 static int
test_dying(struct context * ctx)314 test_dying(struct context *ctx)
315 {
316 pthread_t th;
317 int error;
318
319 kcov_enable(ctx->c_fd, ctx->c_mode);
320
321 if ((error = pthread_create(&th, NULL, closer, (void *)ctx)))
322 errc(1, error, "pthread_create");
323 if ((error = pthread_join(th, NULL)))
324 errc(1, error, "pthread_join");
325
326 error = 0;
327 if (close(ctx->c_fd) == -1) {
328 if (errno != EBADF)
329 err(1, "close");
330 } else {
331 warnx("expected kcov descriptor to be closed");
332 error = 1;
333 }
334 ctx->c_fd = -1;
335
336 return error;
337 }
338
339 /*
340 * Coverage of thread after exec.
341 */
342 static int
test_exec(struct context * ctx)343 test_exec(struct context *ctx)
344 {
345 pid_t pid;
346 int status;
347
348 pid = fork();
349 if (pid == -1)
350 err(1, "fork");
351 if (pid == 0) {
352 kcov_enable(ctx->c_fd, ctx->c_mode);
353 execlp(self, self, "-E", NULL);
354 _exit(1);
355 }
356
357 if (waitpid(pid, &status, 0) == -1)
358 err(1, "waitpid");
359 if (WIFSIGNALED(status)) {
360 warnx("terminated by signal (%d)", WTERMSIG(status));
361 return 1;
362 } else if (WEXITSTATUS(status) != 0) {
363 warnx("non-zero exit (%d)", WEXITSTATUS(status));
364 return 1;
365 }
366
367 /* Upon exit, the kcov descriptor must be reusable again. */
368 kcov_enable(ctx->c_fd, ctx->c_mode);
369 kcov_disable(ctx->c_fd);
370
371 return 0;
372 }
373
374 /*
375 * File descriptor send/receive is not allowed since remote coverage is tied to
376 * the current process.
377 */
378 static int
test_fdsend(struct context * ctx)379 test_fdsend(struct context *ctx)
380 {
381 struct msghdr msg;
382 union {
383 struct cmsghdr hdr;
384 unsigned char buf[CMSG_SPACE(sizeof(int))];
385 } cmsgbuf;
386 struct cmsghdr *cmsg;
387 int pair[2];
388
389 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) == -1)
390 err(1, "socketpair");
391
392 memset(&msg, 0, sizeof(msg));
393 msg.msg_control = &cmsgbuf.buf;
394 msg.msg_controllen = sizeof(cmsgbuf.buf);
395 cmsg = CMSG_FIRSTHDR(&msg);
396 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
397 cmsg->cmsg_level = SOL_SOCKET;
398 cmsg->cmsg_type = SCM_RIGHTS;
399 *(int *)CMSG_DATA(cmsg) = ctx->c_fd;
400 if (sendmsg(pair[1], &msg, 0) != -1)
401 errx(1, "sendmsg: expected error");
402
403 close(pair[0]);
404 close(pair[1]);
405 return 0;
406 }
407
408 /*
409 * Coverage of thread after fork.
410 */
411 static int
test_fork(struct context * ctx)412 test_fork(struct context *ctx)
413 {
414 pid_t pid;
415 int status;
416
417 pid = fork();
418 if (pid == -1)
419 err(1, "fork");
420 if (pid == 0) {
421 kcov_enable(ctx->c_fd, ctx->c_mode);
422 do_syscall();
423 _exit(0);
424 }
425
426 if (waitpid(pid, &status, 0) == -1)
427 err(1, "waitpid");
428 if (WIFSIGNALED(status)) {
429 warnx("terminated by signal (%d)", WTERMSIG(status));
430 return 1;
431 } else if (WEXITSTATUS(status) != 0) {
432 warnx("non-zero exit (%d)", WEXITSTATUS(status));
433 return 1;
434 }
435
436 /* Upon exit, the kcov descriptor must be reusable again. */
437 kcov_enable(ctx->c_fd, ctx->c_mode);
438 kcov_disable(ctx->c_fd);
439
440 return 0;
441 }
442
443 /*
444 * Open /dev/kcov more than once.
445 */
446 static int
test_open(struct context * ctx)447 test_open(struct context *ctx)
448 {
449 unsigned long *cover;
450 int fd;
451 int error = 0;
452
453 fd = kcov_open();
454 if (ioctl(fd, KIOSETBUFSIZE, &ctx->c_bufsize) == -1)
455 err(1, "ioctl: KIOSETBUFSIZE");
456 cover = mmap(NULL, ctx->c_bufsize * sizeof(unsigned long),
457 PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
458 if (cover == MAP_FAILED)
459 err(1, "mmap");
460
461 kcov_enable(fd, ctx->c_mode);
462 do_syscall();
463 kcov_disable(fd);
464
465 error = check_coverage(cover, ctx->c_mode, ctx->c_bufsize, 1);
466
467 if (munmap(cover, ctx->c_bufsize * sizeof(unsigned long)))
468 err(1, "munmap");
469 close(fd);
470
471 return error;
472 }
473
474 /*
475 * Remote taskq coverage. One reliable way to trigger a task on behalf of the
476 * running process is to monitor a kqueue file descriptor using kqueue.
477 */
478 static int
test_remote(struct context * ctx)479 test_remote(struct context *ctx)
480 {
481 struct kio_remote_attach remote = {
482 .subsystem = KCOV_REMOTE_COMMON,
483 .id = 0,
484 };
485 struct kevent kev;
486 int kq1, kq2, pip[2];
487 int x = 0;
488
489 if (ioctl(ctx->c_fd, KIOREMOTEATTACH, &remote) == -1)
490 err(1, "ioctl: KIOREMOTEATTACH");
491 kcov_enable(ctx->c_fd, ctx->c_mode);
492
493 kq1 = kqueue();
494 if (kq1 == -1)
495 err(1, "kqueue");
496 kq2 = kqueue();
497 if (kq1 == -1)
498 err(1, "kqueue");
499 EV_SET(&kev, kq2, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, NULL);
500 if (kevent(kq1, &kev, 1, NULL, 0, NULL) == -1)
501 err(1, "kqueue");
502
503 if (pipe(pip) == -1)
504 err(1, "pipe");
505
506 EV_SET(&kev, pip[0], EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, NULL);
507 if (kevent(kq2, &kev, 1, NULL, 0, NULL) == -1)
508 err(1, "kqueue");
509 (void)write(pip[1], &x, sizeof(x));
510
511 if (kevent(kq1, NULL, 0, &kev, 1, NULL) == -1)
512 err(1, "kevent");
513
514 kcov_disable(ctx->c_fd);
515
516 return 0;
517 }
518
519 /*
520 * Close with remote coverage enabled.
521 */
522 static int
test_remote_close(struct context * ctx)523 test_remote_close(struct context *ctx)
524 {
525 struct kio_remote_attach remote = {
526 .subsystem = KCOV_REMOTE_COMMON,
527 .id = 0,
528 };
529
530 if (ioctl(ctx->c_fd, KIOREMOTEATTACH, &remote) == -1)
531 err(1, "ioctl: KIOREMOTEATTACH");
532 kcov_enable(ctx->c_fd, ctx->c_mode);
533 if (close(ctx->c_fd) == -1)
534 err(1, "close");
535 ctx->c_fd = kcov_open();
536 return 0;
537 }
538
539 /*
540 * Remote interrupt coverage. There's no reliable way to enter a remote section
541 * in interrupt context. This test can however by used to examine the coverage
542 * collected in interrupt context:
543 *
544 * $ until [ -s cov ]; do kcov -v -m pc remote-interrupt >cov; done
545 */
546 static int
test_remote_interrupt(struct context * ctx)547 test_remote_interrupt(struct context *ctx)
548 {
549 struct kio_remote_attach remote = {
550 .subsystem = KCOV_REMOTE_COMMON,
551 .id = 0,
552 };
553 int i;
554
555 if (ioctl(ctx->c_fd, KIOREMOTEATTACH, &remote) == -1)
556 err(1, "ioctl: KIOREMOTEATTACH");
557 kcov_enable(ctx->c_fd, ctx->c_mode);
558
559 for (i = 0; i < 100; i++)
560 (void)getpid();
561
562 kcov_disable(ctx->c_fd);
563
564 return 0;
565 }
566
567 /*
568 * State transitions.
569 */
570 static int
test_state(struct context * ctx)571 test_state(struct context *ctx)
572 {
573 if (ioctl(ctx->c_fd, KIOENABLE, &ctx->c_mode) == -1) {
574 warn("KIOSETBUFSIZE -> KIOENABLE");
575 return 1;
576 }
577 if (ioctl(ctx->c_fd, KIODISABLE) == -1) {
578 warn("KIOENABLE -> KIODISABLE");
579 return 1;
580 }
581 if (ioctl(ctx->c_fd, KIOSETBUFSIZE, 0) != -1) {
582 warnx("KIOSETBUFSIZE -> KIOSETBUFSIZE");
583 return 1;
584 }
585 if (ioctl(ctx->c_fd, KIODISABLE) != -1) {
586 warnx("KIOSETBUFSIZE -> KIODISABLE");
587 return 1;
588 }
589
590 kcov_enable(ctx->c_fd, ctx->c_mode);
591 if (ioctl(ctx->c_fd, KIOENABLE, &ctx->c_mode) != -1) {
592 warnx("KIOENABLE -> KIOENABLE");
593 return 1;
594 }
595 if (ioctl(ctx->c_fd, KIOSETBUFSIZE, 0) != -1) {
596 warnx("KIOENABLE -> KIOSETBUFSIZE");
597 return 1;
598 }
599 kcov_disable(ctx->c_fd);
600
601 return 0;
602 }
603