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 }