forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
kcov provides code coverage collection for coverage-guided fuzzing (randomized testing). Coverage-guided fuzzing is a testing technique that uses coverage feedback to determine new interesting inputs to a system. A notable user-space example is AFL (http://lcamtuf.coredump.cx/afl/). However, this technique is not widely used for kernel testing due to missing compiler and kernel support. 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 or non-interesting parts of kernel is disbled (e.g. scheduler, locking). Currently there is a single coverage collection mode (tracing), but the API anticipates additional collection modes. Initially I also implemented a second mode which exposes coverage in a fixed-size hash table of counters (what Quentin used in his original patch). I've dropped the second mode for simplicity. This patch adds the necessary support on kernel side. The complimentary compiler support was added in gcc revision 231296. We've used this support to build syzkaller system call fuzzer, which has found 90 kernel bugs in just 2 months: https://github.com/google/syzkaller/wiki/Found-Bugs We've also found 30+ bugs in our internal systems with syzkaller. Another (yet unexplored) direction where kcov coverage would greatly help is more traditional "blob mutation". For example, mounting a random blob as a filesystem, or receiving a random blob over wire. Why not gcov. Typical fuzzing loop looks as follows: (1) reset coverage, (2) execute a bit of code, (3) collect coverage, repeat. A typical coverage can be just a dozen of basic blocks (e.g. an invalid input). In such context gcov becomes prohibitively expensive as reset/collect coverage steps depend on total number of basic blocks/edges in program (in case of kernel it is about 2M). Cost of kcov depends only on number of executed basic blocks/edges. On top of that, kernel requires per-thread coverage because there are always background threads and unrelated processes that also produce coverage. With inlined gcov instrumentation per-thread coverage is not possible. kcov exposes kernel PCs and control flow to user-space which is insecure. But debugfs should not be mapped as user accessible. Based on a patch by Quentin Casasnovas. [[email protected]: make task_struct.kcov_mode have type `enum kcov_mode'] [[email protected]: unbreak allmodconfig] [[email protected]: follow x86 Makefile layout standards] Signed-off-by: Dmitry Vyukov <[email protected]> Reviewed-by: Kees Cook <[email protected]> Cc: syzkaller <[email protected]> Cc: Vegard Nossum <[email protected]> Cc: Catalin Marinas <[email protected]> Cc: Tavis Ormandy <[email protected]> Cc: Will Deacon <[email protected]> Cc: Quentin Casasnovas <[email protected]> Cc: Kostya Serebryany <[email protected]> Cc: Eric Dumazet <[email protected]> Cc: Alexander Potapenko <[email protected]> Cc: Kees Cook <[email protected]> Cc: Bjorn Helgaas <[email protected]> Cc: Sasha Levin <[email protected]> Cc: David Drysdale <[email protected]> Cc: Ard Biesheuvel <[email protected]> Cc: Andrey Ryabinin <[email protected]> Cc: Kirill A. Shutemov <[email protected]> Cc: Jiri Slaby <[email protected]> Cc: Ingo Molnar <[email protected]> Cc: Thomas Gleixner <[email protected]> Cc: "H. Peter Anvin" <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
- Loading branch information
Showing
28 changed files
with
567 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
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 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). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
#ifndef _LINUX_KCOV_H | ||
#define _LINUX_KCOV_H | ||
|
||
#include <uapi/linux/kcov.h> | ||
|
||
struct task_struct; | ||
|
||
#ifdef CONFIG_KCOV | ||
|
||
void kcov_task_init(struct task_struct *t); | ||
void kcov_task_exit(struct task_struct *t); | ||
|
||
enum kcov_mode { | ||
/* Coverage collection is not enabled yet. */ | ||
KCOV_MODE_DISABLED = 0, | ||
/* | ||
* Tracing coverage collection mode. | ||
* Covered PCs are collected in a per-task buffer. | ||
*/ | ||
KCOV_MODE_TRACE = 1, | ||
}; | ||
|
||
#else | ||
|
||
static inline void kcov_task_init(struct task_struct *t) {} | ||
static inline void kcov_task_exit(struct task_struct *t) {} | ||
|
||
#endif /* CONFIG_KCOV */ | ||
#endif /* _LINUX_KCOV_H */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
#ifndef _LINUX_KCOV_IOCTLS_H | ||
#define _LINUX_KCOV_IOCTLS_H | ||
|
||
#include <linux/types.h> | ||
|
||
#define KCOV_INIT_TRACE _IOR('c', 1, unsigned long) | ||
#define KCOV_ENABLE _IO('c', 100) | ||
#define KCOV_DISABLE _IO('c', 101) | ||
|
||
#endif /* _LINUX_KCOV_IOCTLS_H */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.