| 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 |
| disbled (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:: |
| |
| #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). |