| kcov: code coverage for fuzzing | |
| =============================== | |
| kcov exposes kernel code coverage information in a form suitable for coverage- | |
| guided fuzzing (randomized testing). Coverage data of a running kernel is | |
| exported via the "kcov" debugfs file. Coverage collection is enabled on a task | |
| basis, and thus it can capture precise coverage of a single system call. | |
| Note that kcov does not aim to collect as much coverage as possible. It aims | |
| to collect more or less stable coverage that is function of syscall inputs. | |
| To achieve this goal it does not collect coverage in soft/hard interrupts | |
| and instrumentation of some inherently non-deterministic parts of kernel is | |
| disabled (e.g. scheduler, locking). | |
| Usage | |
| ----- | |
| Configure the kernel with:: | |
| CONFIG_KCOV=y | |
| CONFIG_KCOV requires gcc built on revision 231296 or later. | |
| Profiling data will only become accessible once debugfs has been mounted:: | |
| mount -t debugfs none /sys/kernel/debug | |
| The following program demonstrates kcov usage from within a test program: | |
| .. code-block:: c | |
| #include <stdio.h> | |
| #include <stddef.h> | |
| #include <stdint.h> | |
| #include <stdlib.h> | |
| #include <sys/types.h> | |
| #include <sys/stat.h> | |
| #include <sys/ioctl.h> | |
| #include <sys/mman.h> | |
| #include <unistd.h> | |
| #include <fcntl.h> | |
| #define KCOV_INIT_TRACE _IOR('c', 1, unsigned long) | |
| #define KCOV_ENABLE _IO('c', 100) | |
| #define KCOV_DISABLE _IO('c', 101) | |
| #define COVER_SIZE (64<<10) | |
| int main(int argc, char **argv) | |
| { | |
| int fd; | |
| unsigned long *cover, n, i; | |
| /* A single fd descriptor allows coverage collection on a single | |
| * thread. | |
| */ | |
| fd = open("/sys/kernel/debug/kcov", O_RDWR); | |
| if (fd == -1) | |
| perror("open"), exit(1); | |
| /* Setup trace mode and trace size. */ | |
| if (ioctl(fd, KCOV_INIT_TRACE, COVER_SIZE)) | |
| perror("ioctl"), exit(1); | |
| /* Mmap buffer shared between kernel- and user-space. */ | |
| cover = (unsigned long*)mmap(NULL, COVER_SIZE * sizeof(unsigned long), | |
| PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); | |
| if ((void*)cover == MAP_FAILED) | |
| perror("mmap"), exit(1); | |
| /* Enable coverage collection on the current thread. */ | |
| if (ioctl(fd, KCOV_ENABLE, 0)) | |
| perror("ioctl"), exit(1); | |
| /* Reset coverage from the tail of the ioctl() call. */ | |
| __atomic_store_n(&cover[0], 0, __ATOMIC_RELAXED); | |
| /* That's the target syscal call. */ | |
| read(-1, NULL, 0); | |
| /* Read number of PCs collected. */ | |
| n = __atomic_load_n(&cover[0], __ATOMIC_RELAXED); | |
| for (i = 0; i < n; i++) | |
| printf("0x%lx\n", cover[i + 1]); | |
| /* Disable coverage collection for the current thread. After this call | |
| * coverage can be enabled for a different thread. | |
| */ | |
| if (ioctl(fd, KCOV_DISABLE, 0)) | |
| perror("ioctl"), exit(1); | |
| /* Free resources. */ | |
| if (munmap(cover, COVER_SIZE * sizeof(unsigned long))) | |
| perror("munmap"), exit(1); | |
| if (close(fd)) | |
| perror("close"), exit(1); | |
| return 0; | |
| } | |
| After piping through addr2line output of the program looks as follows:: | |
| SyS_read | |
| fs/read_write.c:562 | |
| __fdget_pos | |
| fs/file.c:774 | |
| __fget_light | |
| fs/file.c:746 | |
| __fget_light | |
| fs/file.c:750 | |
| __fget_light | |
| fs/file.c:760 | |
| __fdget_pos | |
| fs/file.c:784 | |
| SyS_read | |
| fs/read_write.c:562 | |
| If a program needs to collect coverage from several threads (independently), | |
| it needs to open /sys/kernel/debug/kcov in each thread separately. | |
| The interface is fine-grained to allow efficient forking of test processes. | |
| That is, a parent process opens /sys/kernel/debug/kcov, enables trace mode, | |
| mmaps coverage buffer and then forks child processes in a loop. Child processes | |
| only need to enable coverage (disable happens automatically on thread end). |