github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/tools/kcovtrace/kcovtrace.c (about)

     1  // Copyright 2017 syzkaller project authors. All rights reserved.
     2  // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
     3  
     4  // kcovtrace is like strace but show kernel coverage collected with KCOV.
     5  // It is very simplistic at this point and does not support multithreaded processes, etc.
     6  // It can be used to understand, for example, exact location where kernel bails out
     7  // with an error for a particular syscall.
     8  
     9  #include <fcntl.h>
    10  #include <stddef.h>
    11  #include <stdint.h>
    12  #include <stdio.h>
    13  #include <stdlib.h>
    14  #include <sys/ioctl.h>
    15  #include <sys/mman.h>
    16  #include <sys/stat.h>
    17  #include <sys/types.h>
    18  #include <sys/wait.h>
    19  #include <unistd.h>
    20  
    21  #if defined(__FreeBSD__) || defined(__NetBSD__)
    22  #include <sys/kcov.h>
    23  #define KCOV_PATH "/dev/kcov"
    24  typedef uint64_t cover_t;
    25  #else
    26  #define KCOV_INIT_TRACE _IOR('c', 1, unsigned long)
    27  #define KCOV_ENABLE _IO('c', 100)
    28  #define KCOV_DISABLE _IO('c', 101)
    29  #define KCOV_ENTRY_SIZE sizeof(unsigned long)
    30  #define KCOV_PATH "/sys/kernel/debug/kcov"
    31  #define KCOV_TRACE_PC 0
    32  typedef unsigned long cover_t;
    33  #endif
    34  #define COVER_SIZE (16 << 20)
    35  
    36  int main(int argc, char** argv, char** envp)
    37  {
    38  	int fd, pid, status;
    39  	cover_t *cover, n, i;
    40  
    41  	if (argc == 1)
    42  		fprintf(stderr, "usage: kcovtrace program [args...]\n"), exit(1);
    43  	fd = open(KCOV_PATH, O_RDWR);
    44  	if (fd == -1)
    45  		perror("open"), exit(1);
    46  #if defined(__FreeBSD__)
    47  	if (ioctl(fd, KIOSETBUFSIZE, COVER_SIZE))
    48  #elif defined(__NetBSD__)
    49  	uint64_t cover_size = COVER_SIZE;
    50  	if (ioctl(fd, KCOV_IOC_SETBUFSIZE, &cover_size))
    51  #else
    52  	if (ioctl(fd, KCOV_INIT_TRACE, COVER_SIZE))
    53  #endif
    54  		perror("ioctl"), exit(1);
    55  	cover = (cover_t*)mmap(NULL, COVER_SIZE * KCOV_ENTRY_SIZE,
    56  			       PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    57  	if ((void*)cover == MAP_FAILED)
    58  		perror("mmap"), exit(1);
    59  	pid = fork();
    60  	if (pid < 0)
    61  		perror("fork"), exit(1);
    62  	if (pid == 0) {
    63  #if defined(__FreeBSD__)
    64  		if (ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_PC))
    65  #elif defined(__NetBSD__)
    66  		int kcov_mode = KCOV_MODE_TRACE_PC;
    67  		if (ioctl(cov->fd, KCOV_IOC_ENABLE, &kcov_mode))
    68  #else
    69  		if (ioctl(fd, KCOV_ENABLE, KCOV_TRACE_PC))
    70  #endif
    71  			perror("ioctl"), exit(1);
    72  		__atomic_store_n(&cover[0], 0, __ATOMIC_RELAXED);
    73  		execve(argv[1], argv + 1, envp);
    74  		perror("execve");
    75  		exit(1);
    76  	}
    77  #if defined(__FreeBSD__)
    78  	while (waitpid(-1, &status, 0) != pid) {
    79  #else
    80  	while (waitpid(-1, &status, __WALL) != pid) {
    81  #endif
    82  	}
    83  	n = __atomic_load_n(&cover[0], __ATOMIC_RELAXED);
    84  	for (i = 0; i < n; i++)
    85  		printf("0x%jx\n", (uintmax_t)cover[i + 1]);
    86  	if (munmap(cover, COVER_SIZE * KCOV_ENTRY_SIZE))
    87  		perror("munmap"), exit(1);
    88  	if (close(fd))
    89  		perror("close"), exit(1);
    90  	return 0;
    91  }