123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112 |
- 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).
|