aboutsummaryrefslogtreecommitdiffstats
path: root/tools/kcovfuzzer/kcovfuzzer.c
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2019-09-19 15:56:24 +0200
committerDmitry Vyukov <dvyukov@google.com>2019-09-19 15:56:24 +0200
commit2dd371a3b14d0d8fe6afcd8bcda65d5575c0bf3d (patch)
treef50cbbc53ae332eb8cd49df1dbc10fbc8fb39286 /tools/kcovfuzzer/kcovfuzzer.c
parent408e453738232a4cd8281ff6684648d7b5ff23af (diff)
tools/kcovfuzzer: add simple KCOV/libfuzzer glue
Diffstat (limited to 'tools/kcovfuzzer/kcovfuzzer.c')
-rw-r--r--tools/kcovfuzzer/kcovfuzzer.c128
1 files changed, 128 insertions, 0 deletions
diff --git a/tools/kcovfuzzer/kcovfuzzer.c b/tools/kcovfuzzer/kcovfuzzer.c
new file mode 100644
index 000000000..ae3355b72
--- /dev/null
+++ b/tools/kcovfuzzer/kcovfuzzer.c
@@ -0,0 +1,128 @@
+// KCOV glue for libfuzzer. Build as:
+// clang libfuzzer_kcov.c -fsanitize=fuzzer -Wall -o fuzzer
+// Run as:
+// ./fuzzer -max_len=129 corpus-bpf
+// ./fuzzer -max_len=100 -only_ascii=1 corpus-filter
+// If you need to build with -static, then the following env needs to be exported:
+// UBSAN_OPTIONS="handle_segv=0 handle_sigbus=0 handle_abort=0 handle_sigill=0 handle_sigtrap=0 handle_sigfpe=0"
+// and the following flags added to fuzzer invocation:
+// -timeout=0 -rss_limit_mb=0 -handle_segv=0 -handle_bus=0 -handle_abrt=0 -handle_ill=0 \
+// -handle_fpe=0 -handle_int=0 -handle_term=0 -handle_xfsz=0 -handle_usr1=0 -handle_usr2=0
+
+#define _GNU_SOURCE
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/bpf.h>
+#include <memory.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define KCOV_COVER_SIZE (256 << 10)
+#define KCOV_TRACE_PC 0
+#define KCOV_INIT_TRACE64 _IOR('c', 1, uint64_t)
+#define KCOV_ENABLE _IO('c', 100)
+
+__attribute__((section("__libfuzzer_extra_counters"))) unsigned char libfuzzer_coverage[32 << 10];
+
+void fail(const char* msg, ...)
+{
+ int e = errno;
+ va_list args;
+ va_start(args, msg);
+ vfprintf(stderr, msg, args);
+ va_end(args);
+ fprintf(stderr, " (errno %d)\n", e);
+ _exit(1);
+}
+
+#define FUZZ_BPF 1
+//#define FUZZ_TRACE_FILTER 1
+
+int LLVMFuzzerTestOneInput(const char* data, long size)
+{
+ static int kcov = -1;
+ static uint64_t* kcov_data;
+ if (kcov == -1) {
+ kcov = open("/sys/kernel/debug/kcov", O_RDWR);
+ if (kcov == -1)
+ fail("open of /sys/kernel/debug/kcov failed");
+ if (ioctl(kcov, KCOV_INIT_TRACE64, KCOV_COVER_SIZE))
+ fail("cover init trace write failed");
+ kcov_data = (uint64_t*)mmap(NULL, KCOV_COVER_SIZE * sizeof(kcov_data[0]),
+ PROT_READ | PROT_WRITE, MAP_SHARED, kcov, 0);
+ if (kcov_data == MAP_FAILED)
+ fail("cover mmap failed");
+ if (ioctl(kcov, KCOV_ENABLE, KCOV_TRACE_PC))
+ fail("cover enable write trace failed");
+ }
+#ifdef FUZZ_BPF
+ union bpf_attr attr;
+ memset(&attr, 0, sizeof(attr));
+ attr.map_type = BPF_MAP_TYPE_HASH;
+ attr.key_size = 8;
+ attr.value_size = 8;
+ attr.max_entries = 2;
+ int mfd = syscall(SYS_bpf, BPF_MAP_CREATE, &attr, sizeof(attr));
+
+ memset(&attr, 0, sizeof(attr));
+ attr.prog_type = BPF_PROG_TYPE_SOCKET_FILTER;
+ attr.insns = (uint64_t)data;
+ attr.insn_cnt = size / 8;
+ attr.license = (uint64_t) "GPL";
+
+ __atomic_store_n(&kcov_data[0], 0, __ATOMIC_RELAXED);
+ int pfd = syscall(SYS_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
+ if (pfd != -1) {
+ memset(&attr, 0, sizeof(attr));
+ attr.test.prog_fd = pfd;
+ syscall(SYS_bpf, BPF_PROG_TEST_RUN, &attr, sizeof(attr));
+ }
+#elif FUZZ_TRACE_FILTER
+ int fd0 = open("/sys/kernel/debug/tracing/events/syscalls/sys_exit_read/enable", O_RDWR);
+ if (fd0 == -1)
+ fail("open enable failed");
+ int fd1 = open("/sys/kernel/debug/tracing/events/syscalls/sys_exit_read/filter", O_RDWR);
+ if (fd1 == -1)
+ fail("open filter failed");
+ int fd2 = open("/sys/kernel/debug/tracing/events/syscalls/sys_exit_read/trigger", O_RDWR);
+ if (fd2 == -1)
+ fail("open trigger failed");
+ __atomic_store_n(&kcov_data[0], 0, __ATOMIC_RELAXED);
+ char buf[256];
+ buf[0] = '1';
+ write(fd0, buf, 1);
+ write(fd1, data, size);
+ read(fd1, buf, sizeof(buf));
+ buf[0] = '0';
+ write(fd1, buf, 1);
+ write(fd2, data, size);
+ read(fd2, buf, sizeof(buf));
+ buf[0] = '0';
+ write(fd0, buf, 1);
+#endif
+ uint64_t ncov = __atomic_load_n(&kcov_data[0], __ATOMIC_RELAXED);
+ if (ncov >= KCOV_COVER_SIZE)
+ fail("too much cover: %llu", ncov);
+ for (uint64_t i = 0; i < ncov; i++) {
+ uint64_t pc = __atomic_load_n(&kcov_data[i + 1], __ATOMIC_RELAXED);
+ libfuzzer_coverage[pc % sizeof(libfuzzer_coverage)]++;
+ }
+#ifdef FUZZ_BPF
+
+ close(pfd);
+ close(mfd);
+#elif FUZZ_TRACE_FILTER
+ close(fd0);
+ close(fd1);
+ close(fd2);
+#endif
+ return 0;
+}