You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
113 lines
3.6 KiB
113 lines
3.6 KiB
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).
|
|
|