blob: ae3355b72cf38bf5d1480a6bc511c91950137409 [file] [log] [blame]
// 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;
}