github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/tools/kcovfuzzer/kcovfuzzer.c (about)

     1  // KCOV glue for libfuzzer. Build as:
     2  // clang tools/kcovfuzzer/kcovfuzzer.c -fsanitize=fuzzer -static -Wall -o fuzzer
     3  // Run as:
     4  //
     5  // KCOVFUZZER=bpf ./fuzzer -max_len=129 corpus_bpf
     6  // KCOVFUZZER=trace_filter ./fuzzer -max_len=100 -only_ascii=1 corpus_trace_filter
     7  // KCOVFUZZER=binfmt ./fuzzer -max_len=30 -only_ascii=1 corpus_binfmt
     8  //
     9  // If you build with -static, then the following env needs to be exported:
    10  // UBSAN_OPTIONS="handle_segv=0 handle_sigbus=0 handle_abort=0 handle_sigill=0 handle_sigtrap=0 handle_sigfpe=0"
    11  // and the following flags added to fuzzer invocation:
    12  // -timeout=0 -rss_limit_mb=0 -handle_segv=0 -handle_bus=0 -handle_abrt=0 -handle_ill=0 \
    13  // -handle_fpe=0 -handle_int=0 -handle_term=0 -handle_xfsz=0 -handle_usr1=0 -handle_usr2=0
    14  
    15  #define _GNU_SOURCE
    16  #include <errno.h>
    17  #include <fcntl.h>
    18  #include <linux/bpf.h>
    19  #include <memory.h>
    20  #include <stdarg.h>
    21  #include <stdint.h>
    22  #include <stdio.h>
    23  #include <stdlib.h>
    24  #include <sys/ioctl.h>
    25  #include <sys/mman.h>
    26  #include <sys/stat.h>
    27  #include <sys/syscall.h>
    28  #include <sys/types.h>
    29  #include <unistd.h>
    30  
    31  void init(const char*, long);
    32  void dump_input(const char*, long);
    33  void (*fuzz_func)(const char*, long) = init;
    34  void fail(const char* msg, ...);
    35  void cover_start();
    36  void cover_stop();
    37  
    38  int LLVMFuzzerTestOneInput(const char* data, long size)
    39  {
    40  	dump_input(data, size);
    41  	fuzz_func(data, size);
    42  	return 0;
    43  }
    44  
    45  void bpf(const char* data, long size)
    46  {
    47  	union bpf_attr attr;
    48  	memset(&attr, 0, sizeof(attr));
    49  	attr.map_type = BPF_MAP_TYPE_HASH;
    50  	attr.key_size = 8;
    51  	attr.value_size = 8;
    52  	attr.max_entries = 2;
    53  	int mfd = syscall(SYS_bpf, BPF_MAP_CREATE, &attr, sizeof(attr));
    54  
    55  	memset(&attr, 0, sizeof(attr));
    56  	attr.prog_type = BPF_PROG_TYPE_SOCKET_FILTER;
    57  	attr.insns = (uint64_t)data;
    58  	attr.insn_cnt = size / 8;
    59  	attr.license = (uint64_t) "GPL";
    60  
    61  	cover_start();
    62  	int pfd = syscall(SYS_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
    63  	if (pfd != -1) {
    64  		memset(&attr, 0, sizeof(attr));
    65  		attr.test.prog_fd = pfd;
    66  		syscall(SYS_bpf, BPF_PROG_TEST_RUN, &attr, sizeof(attr));
    67  	}
    68  	cover_stop();
    69  	close(pfd);
    70  	close(mfd);
    71  }
    72  
    73  void trace_filter(const char* data, long size)
    74  {
    75  	int fd0 = open("/sys/kernel/debug/tracing/events/syscalls/sys_exit_read/enable", O_RDWR);
    76  	if (fd0 == -1)
    77  		fail("open enable failed");
    78  	int fd1 = open("/sys/kernel/debug/tracing/events/syscalls/sys_exit_read/filter", O_RDWR);
    79  	if (fd1 == -1)
    80  		fail("open filter failed");
    81  	int fd2 = open("/sys/kernel/debug/tracing/events/syscalls/sys_exit_read/trigger", O_RDWR);
    82  	if (fd2 == -1)
    83  		fail("open trigger failed");
    84  	cover_start();
    85  	char buf[256];
    86  	buf[0] = '1';
    87  	write(fd0, buf, 1);
    88  	write(fd1, data, size);
    89  	read(fd1, buf, sizeof(buf));
    90  	buf[0] = '0';
    91  	write(fd1, buf, 1);
    92  	write(fd2, data, size);
    93  	read(fd2, buf, sizeof(buf));
    94  	buf[0] = '0';
    95  	write(fd0, buf, 1);
    96  	cover_stop();
    97  	close(fd0);
    98  	close(fd1);
    99  	close(fd2);
   100  }
   101  
   102  void binfmt(const char* data, long size)
   103  {
   104  	static int fd = -1;
   105  	if (fd == -1)
   106  		fd = open("/proc/sys/fs/binfmt_misc/register", O_WRONLY);
   107  	if (fd == -1)
   108  		fail("open(/proc/sys/fs/binfmt_misc/register) failed");
   109  	cover_start();
   110  	write(fd, data, size);
   111  	cover_stop();
   112  }
   113  
   114  #define KCOV_COVER_SIZE (256 << 10)
   115  #define KCOV_TRACE_PC 0
   116  #define KCOV_INIT_TRACE64 _IOR('c', 1, uint64_t)
   117  #define KCOV_ENABLE _IO('c', 100)
   118  
   119  __attribute__((section("__libfuzzer_extra_counters"))) unsigned char libfuzzer_coverage[32 << 10];
   120  uint64_t* kcov_data;
   121  
   122  void init(const char* data, long size)
   123  {
   124  	const char* name = getenv("KCOVFUZZER");
   125  	if (strcmp(name, "bpf") == 0)
   126  		fuzz_func = bpf;
   127  	else if (strcmp(name, "trace_filter") == 0)
   128  		fuzz_func = trace_filter;
   129  	else if (strcmp(name, "binfmt") == 0)
   130  		fuzz_func = binfmt;
   131  	else
   132  		fail("unknown fuzz function '%s'", name);
   133  
   134  	int kcov = open("/sys/kernel/debug/kcov", O_RDWR);
   135  	if (kcov == -1)
   136  		fail("open of /sys/kernel/debug/kcov failed");
   137  	if (ioctl(kcov, KCOV_INIT_TRACE64, KCOV_COVER_SIZE))
   138  		fail("cover init trace write failed");
   139  	kcov_data = (uint64_t*)mmap(NULL, KCOV_COVER_SIZE * sizeof(kcov_data[0]),
   140  				    PROT_READ | PROT_WRITE, MAP_SHARED, kcov, 0);
   141  	if (kcov_data == MAP_FAILED)
   142  		fail("cover mmap failed");
   143  	if (ioctl(kcov, KCOV_ENABLE, KCOV_TRACE_PC))
   144  		fail("cover enable write trace failed");
   145  	close(kcov);
   146  
   147  	fuzz_func(data, size);
   148  }
   149  
   150  void cover_start()
   151  {
   152  	__atomic_store_n(&kcov_data[0], 0, __ATOMIC_RELAXED);
   153  }
   154  
   155  void cover_stop()
   156  {
   157  	uint64_t ncov = __atomic_load_n(&kcov_data[0], __ATOMIC_RELAXED);
   158  	if (ncov >= KCOV_COVER_SIZE)
   159  		fail("too much cover: %llu", ncov);
   160  	for (uint64_t i = 0; i < ncov; i++) {
   161  		uint64_t pc = __atomic_load_n(&kcov_data[i + 1], __ATOMIC_RELAXED);
   162  		libfuzzer_coverage[pc % sizeof(libfuzzer_coverage)]++;
   163  	}
   164  }
   165  
   166  void dump_input(const char* data, long size)
   167  {
   168  	static int kmsg = -1;
   169  	if (kmsg == -1) {
   170  		kmsg = open("/dev/kmsg", O_WRONLY);
   171  		if (kmsg == -1)
   172  			fail("open(/dev/kmsg) failed");
   173  		int printk_devkmsg = open("/proc/sys/kernel/printk_devkmsg", O_WRONLY);
   174  		if (printk_devkmsg == -1)
   175  			fail("open(/proc/sys/kernel/printk_devkmsg) failed");
   176  		if (write(printk_devkmsg, "on", 3) != 3)
   177  			fail("write(/proc/sys/kernel/printk_devkmsg) failed");
   178  		close(printk_devkmsg);
   179  	}
   180  	char buf[1024];
   181  	char* pos = buf + sprintf(buf, "INPUT[%ld]: ", size);
   182  	for (long i = 0; i < size; i++) {
   183  		if (pos > buf + sizeof(buf) - 10) {
   184  			*pos++ = '.';
   185  			*pos++ = '.';
   186  			*pos++ = '.';
   187  			break;
   188  		}
   189  		char ch = data[i];
   190  		if (ch >= 0x20 && ch < 0x7f && ch != '\\') {
   191  			*pos++ = ch;
   192  			continue;
   193  		}
   194  		*pos++ = '\\';
   195  		*pos++ = 'x';
   196  		char hi = ch >> 4;
   197  		if (hi <= 9)
   198  			*pos++ = '0' + hi;
   199  		else
   200  			*pos++ = 'a' + hi - 9;
   201  		char lo = ch & 0xf;
   202  		if (lo <= 9)
   203  			*pos++ = '0' + lo;
   204  		else
   205  			*pos++ = 'a' + lo - 9;
   206  	}
   207  	*pos++ = '\n';
   208  	if (write(kmsg, buf, pos - buf) != pos - buf)
   209  		fail("write(/dev/kmsg) failed");
   210  }
   211  
   212  void fail(const char* msg, ...)
   213  {
   214  	int e = errno;
   215  	va_list args;
   216  	va_start(args, msg);
   217  	vfprintf(stderr, msg, args);
   218  	va_end(args);
   219  	fprintf(stderr, " (errno %d)\n", e);
   220  	_exit(1);
   221  }