diff options
Diffstat (limited to 'instrumentation/afl-compiler-rt.o.c')
-rw-r--r-- | instrumentation/afl-compiler-rt.o.c | 290 |
1 files changed, 244 insertions, 46 deletions
diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index ecb94cab..cca38cd0 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -20,7 +20,7 @@ #include "config.h" #include "types.h" #include "cmplog.h" -#include "llvm-ngram-coverage.h" +#include "llvm-alternative-coverage.h" #include <stdio.h> #include <stdlib.h> @@ -34,6 +34,7 @@ #include <errno.h> #include <sys/mman.h> +#include <sys/syscall.h> #ifndef __HAIKU__ #include <sys/shm.h> #endif @@ -96,10 +97,12 @@ int __afl_selective_coverage_temp = 1; #if defined(__ANDROID__) || defined(__HAIKU__) PREV_LOC_T __afl_prev_loc[NGRAM_SIZE_MAX]; +PREV_LOC_T __afl_prev_caller[CTX_MAX_K]; u32 __afl_prev_ctx; u32 __afl_cmp_counter; #else __thread PREV_LOC_T __afl_prev_loc[NGRAM_SIZE_MAX]; +__thread PREV_LOC_T __afl_prev_caller[CTX_MAX_K]; __thread u32 __afl_prev_ctx; __thread u32 __afl_cmp_counter; #endif @@ -122,6 +125,21 @@ static u8 is_persistent; static u8 _is_sancov; +/* Debug? */ + +static u32 __afl_debug; + +/* Already initialized markers */ + +u32 __afl_already_initialized_shm; +u32 __afl_already_initialized_forkserver; +u32 __afl_already_initialized_first; +u32 __afl_already_initialized_second; + +/* Dummy pipe for area_is_valid() */ + +static int __afl_dummy_fd[2] = {2, 2}; + /* ensure we kill the child on termination */ void at_exit(int signal) { @@ -171,7 +189,7 @@ static void __afl_map_shm_fuzz() { char *id_str = getenv(SHM_FUZZ_ENV_VAR); - if (getenv("AFL_DEBUG")) { + if (__afl_debug) { fprintf(stderr, "DEBUG: fuzzcase shmem %s\n", id_str ? id_str : "none"); @@ -217,7 +235,7 @@ static void __afl_map_shm_fuzz() { __afl_fuzz_len = (u32 *)map; __afl_fuzz_ptr = map + sizeof(u32); - if (getenv("AFL_DEBUG")) { + if (__afl_debug) { fprintf(stderr, "DEBUG: successfully got fuzzing shared memory\n"); @@ -237,6 +255,9 @@ static void __afl_map_shm_fuzz() { static void __afl_map_shm(void) { + if (__afl_already_initialized_shm) return; + __afl_already_initialized_shm = 1; + // if we are not running in afl ensure the map exists if (!__afl_area_ptr) { __afl_area_ptr = __afl_area_ptr_dummy; } @@ -294,18 +315,23 @@ static void __afl_map_shm(void) { early-stage __afl_area_initial region that is needed to allow some really hacky .init code to work correctly in projects such as OpenSSL. */ - if (getenv("AFL_DEBUG")) + if (__afl_debug) { + fprintf(stderr, - "DEBUG: id_str %s, __afl_area_ptr %p, __afl_area_initial %p, " - "__afl_map_addr 0x%llx, MAP_SIZE %u, __afl_final_loc %u, " + "DEBUG: (1) id_str %s, __afl_area_ptr %p, __afl_area_initial %p, " + "__afl_area_ptr_dummy 0x%p, __afl_map_addr 0x%llx, MAP_SIZE %u, " + "__afl_final_loc %u, " "max_size_forkserver %u/0x%x\n", id_str == NULL ? "<null>" : id_str, __afl_area_ptr, - __afl_area_initial, __afl_map_addr, MAP_SIZE, __afl_final_loc, - FS_OPT_MAX_MAPSIZE, FS_OPT_MAX_MAPSIZE); + __afl_area_initial, __afl_area_ptr_dummy, __afl_map_addr, MAP_SIZE, + __afl_final_loc, FS_OPT_MAX_MAPSIZE, FS_OPT_MAX_MAPSIZE); + + } if (id_str) { - if (__afl_area_ptr && __afl_area_ptr != __afl_area_initial) { + if (__afl_area_ptr && __afl_area_ptr != __afl_area_initial && + __afl_area_ptr != __afl_area_ptr_dummy) { if (__afl_map_addr) { @@ -350,17 +376,18 @@ static void __afl_map_shm(void) { } - if (shm_base == MAP_FAILED) { + close(shm_fd); + shm_fd = -1; - close(shm_fd); - shm_fd = -1; + if (shm_base == MAP_FAILED) { fprintf(stderr, "mmap() failed\n"); + perror("mmap for map"); + if (__afl_map_addr) send_forkserver_error(FS_ERROR_MAP_ADDR); else send_forkserver_error(FS_ERROR_MMAP); - perror("mmap for map"); exit(2); @@ -439,6 +466,19 @@ static void __afl_map_shm(void) { __afl_area_ptr_backup = __afl_area_ptr; + if (__afl_debug) { + + fprintf(stderr, + "DEBUG: (2) id_str %s, __afl_area_ptr %p, __afl_area_initial %p, " + "__afl_area_ptr_dummy 0x%p, __afl_map_addr 0x%llx, MAP_SIZE " + "%u, __afl_final_loc %u, " + "max_size_forkserver %u/0x%x\n", + id_str == NULL ? "<null>" : id_str, __afl_area_ptr, + __afl_area_initial, __afl_area_ptr_dummy, __afl_map_addr, MAP_SIZE, + __afl_final_loc, FS_OPT_MAX_MAPSIZE, FS_OPT_MAX_MAPSIZE); + + } + if (__afl_selective_coverage) { if (__afl_map_size > MAP_INITIAL_SIZE) { @@ -467,7 +507,7 @@ static void __afl_map_shm(void) { id_str = getenv(CMPLOG_SHM_ENV_VAR); - if (getenv("AFL_DEBUG")) { + if (__afl_debug) { fprintf(stderr, "DEBUG: cmplog id_str %s\n", id_str == NULL ? "<null>" : id_str); @@ -476,6 +516,12 @@ static void __afl_map_shm(void) { if (id_str) { + if ((__afl_dummy_fd[1] = open("/dev/null", O_WRONLY)) < 0) { + + if (pipe(__afl_dummy_fd) < 0) { __afl_dummy_fd[1] = 1; } + + } + #ifdef USEMMAP const char * shm_file_path = id_str; int shm_fd = -1; @@ -526,6 +572,58 @@ static void __afl_map_shm(void) { } +/* unmap SHM. */ + +static void __afl_unmap_shm(void) { + + if (!__afl_already_initialized_shm) return; + + char *id_str = getenv(SHM_ENV_VAR); + + if (id_str) { + +#ifdef USEMMAP + + munmap((void *)__afl_area_ptr, __afl_map_size); + +#else + + shmdt((void *)__afl_area_ptr); + +#endif + + } else if ((!__afl_area_ptr || __afl_area_ptr == __afl_area_initial) && + + __afl_map_addr) { + + munmap((void *)__afl_map_addr, __afl_map_size); + + } + + __afl_area_ptr = __afl_area_ptr_dummy; + + id_str = getenv(CMPLOG_SHM_ENV_VAR); + + if (id_str) { + +#ifdef USEMMAP + + munmap((void *)__afl_cmp_map, __afl_map_size); + +#else + + shmdt((void *)__afl_cmp_map); + +#endif + + __afl_cmp_map = NULL; + + } + + __afl_already_initialized_shm = 0; + +} + #ifdef __linux__ static void __afl_start_snapshots(void) { @@ -554,7 +652,7 @@ static void __afl_start_snapshots(void) { if (read(FORKSRV_FD, &was_killed, 4) != 4) { _exit(1); } - if (getenv("AFL_DEBUG")) { + if (__afl_debug) { fprintf(stderr, "target forkserver recv: %08x\n", was_killed); @@ -731,6 +829,9 @@ static void __afl_start_snapshots(void) { static void __afl_start_forkserver(void) { + if (__afl_already_initialized_forkserver) return; + __afl_already_initialized_forkserver = 1; + struct sigaction orig_action; sigaction(SIGTERM, NULL, &orig_action); old_sigterm_handler = orig_action.sa_handler; @@ -781,7 +882,7 @@ static void __afl_start_forkserver(void) { if (read(FORKSRV_FD, &was_killed, 4) != 4) _exit(1); - if (getenv("AFL_DEBUG")) { + if (__afl_debug) { fprintf(stderr, "target forkserver recv: %08x\n", was_killed); @@ -1016,11 +1117,14 @@ void __afl_manual_init(void) { __afl_sharedmem_fuzzing = 0; if (__afl_area_ptr == NULL) __afl_area_ptr = __afl_area_ptr_dummy; - if (getenv("AFL_DEBUG")) + if (__afl_debug) { + fprintf(stderr, "DEBUG: disabled instrumentation because of " "AFL_DISABLE_LLVM_INSTRUMENTATION\n"); + } + } if (!init_done) { @@ -1060,6 +1164,11 @@ __attribute__((constructor(CTOR_PRIO))) void __afl_auto_early(void) { __attribute__((constructor(1))) void __afl_auto_second(void) { + if (__afl_already_initialized_second) return; + __afl_already_initialized_second = 1; + + if (getenv("AFL_DEBUG")) { __afl_debug = 1; } + if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) return; u8 *ptr; @@ -1084,17 +1193,18 @@ __attribute__((constructor(1))) void __afl_auto_second(void) { } -} +} // ptr memleak report is a false positive /* preset __afl_area_ptr #1 - at constructor level 0 global variables have not been set */ __attribute__((constructor(0))) void __afl_auto_first(void) { - if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) return; - u8 *ptr; + if (__afl_already_initialized_first) return; + __afl_already_initialized_first = 1; - ptr = (u8 *)malloc(MAP_INITIAL_SIZE); + if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) return; + u8 *ptr = (u8 *)malloc(MAP_INITIAL_SIZE); if (ptr && (ssize_t)ptr != -1) { @@ -1103,7 +1213,7 @@ __attribute__((constructor(0))) void __afl_auto_first(void) { } -} +} // ptr memleak report is a false positive /* The following stuff deals with supporting -fsanitize-coverage=trace-pc-guard. It remains non-operational in the traditional, plugin-backed LLVM mode. @@ -1171,11 +1281,13 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { _is_sancov = 1; - if (getenv("AFL_DEBUG")) { + if (__afl_debug) { fprintf(stderr, - "Running __sanitizer_cov_trace_pc_guard_init: %p-%p (%lu edges)\n", - start, stop, (unsigned long)(stop - start)); + "Running __sanitizer_cov_trace_pc_guard_init: %p-%p (%lu edges) " + "after_fs=%u\n", + start, stop, (unsigned long)(stop - start), + __afl_already_initialized_forkserver); } @@ -1191,6 +1303,40 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { } + /* instrumented code is loaded *after* our forkserver is up. this is a + problem. We cannot prevent collisions then :( */ + if (__afl_already_initialized_forkserver && + __afl_final_loc + 1 + stop - start > __afl_map_size) { + + if (__afl_debug) { + + fprintf(stderr, "Warning: new instrumented code after the forkserver!\n"); + + } + + __afl_final_loc = 2; + + if (1 + stop - start > __afl_map_size) { + + *(start++) = ++__afl_final_loc; + + while (start < stop) { + + if (R(100) < inst_ratio) + *start = ++__afl_final_loc % __afl_map_size; + else + *start = 0; + + start++; + + } + + return; + + } + + } + /* Make sure that the first element in the range is always set - we use that to avoid duplicate calls (which can happen as an artifact of the underlying implementation in LLVM). */ @@ -1208,6 +1354,28 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { } + if (__afl_debug) { + + fprintf(stderr, + "Done __sanitizer_cov_trace_pc_guard_init: __afl_final_loc = %u\n", + __afl_final_loc); + + } + + if (__afl_already_initialized_shm && __afl_final_loc > __afl_map_size) { + + if (__afl_debug) { + + fprintf(stderr, "Reinit shm necessary (+%u)\n", + __afl_final_loc - __afl_map_size); + + } + + __afl_unmap_shm(); + __afl_map_shm(); + + } + } ///// CmpLog instrumentation @@ -1551,17 +1719,42 @@ void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) { } +__attribute__((weak)) void *__asan_region_is_poisoned(void *beg, size_t size) { + + return NULL; + +} + // POSIX shenanigan to see if an area is mapped. // If it is mapped as X-only, we have a problem, so maybe we should add a check // to avoid to call it on .text addresses -static int area_is_mapped(void *ptr, size_t len) { +static int area_is_valid(void *ptr, size_t len) { + + if (unlikely(__asan_region_is_poisoned(ptr, len))) { return 0; } - char *p = (char *)ptr; - char *page = (char *)((uintptr_t)p & ~(sysconf(_SC_PAGE_SIZE) - 1)); + long r = syscall(__afl_dummy_fd[1], SYS_write, ptr, len); - int r = msync(page, (p - page) + len, MS_ASYNC); - if (r < 0) return errno != ENOMEM; - return 1; + if (unlikely(r <= 0 || r > len)) { // fail - maybe hitting asan boundary? + + char *p = (char *)ptr; + long page_size = sysconf(_SC_PAGE_SIZE); + char *page = (char *)((uintptr_t)p & ~(page_size - 1)) + page_size; + if (page < p + len) { return 0; } // no isnt, return fail + len -= (p + len - page); + r = syscall(__afl_dummy_fd[1], SYS_write, p, len); + + } + + // partial writes - we return what was written. + if (likely(r >= 0 && r <= len)) { + + return (int)r; + + } else { + + return 0; + + } } @@ -1569,19 +1762,22 @@ void __cmplog_rtn_hook(u8 *ptr1, u8 *ptr2) { /* u32 i; - if (!area_is_mapped(ptr1, 32) || !area_is_mapped(ptr2, 32)) return; + if (area_is_valid(ptr1, 32) <= 0 || area_is_valid(ptr2, 32) <= 0) return; fprintf(stderr, "rtn arg0="); - for (i = 0; i < 24; i++) + for (i = 0; i < 32; i++) fprintf(stderr, "%02x", ptr1[i]); fprintf(stderr, " arg1="); - for (i = 0; i < 24; i++) + for (i = 0; i < 32; i++) fprintf(stderr, "%02x", ptr2[i]); fprintf(stderr, "\n"); */ if (unlikely(!__afl_cmp_map)) return; - - if (!area_is_mapped(ptr1, 32) || !area_is_mapped(ptr2, 32)) return; + int l1, l2; + if ((l1 = area_is_valid(ptr1, 32)) <= 0 || + (l2 = area_is_valid(ptr2, 32)) <= 0) + return; + int len = MIN(l1, l2); uintptr_t k = (uintptr_t)__builtin_return_address(0); k = (k >> 4) ^ (k << 8); @@ -1592,17 +1788,17 @@ void __cmplog_rtn_hook(u8 *ptr1, u8 *ptr2) { if (__afl_cmp_map->headers[k].type != CMP_TYPE_RTN) { __afl_cmp_map->headers[k].type = CMP_TYPE_RTN; - hits = 0; __afl_cmp_map->headers[k].hits = 1; - __afl_cmp_map->headers[k].shape = 31; + __afl_cmp_map->headers[k].shape = len - 1; + hits = 0; } else { hits = __afl_cmp_map->headers[k].hits++; - if (__afl_cmp_map->headers[k].shape < 31) { + if (__afl_cmp_map->headers[k].shape < len) { - __afl_cmp_map->headers[k].shape = 31; + __afl_cmp_map->headers[k].shape = len - 1; } @@ -1610,9 +1806,9 @@ void __cmplog_rtn_hook(u8 *ptr1, u8 *ptr2) { hits &= CMP_MAP_RTN_H - 1; __builtin_memcpy(((struct cmpfn_operands *)__afl_cmp_map->log[k])[hits].v0, - ptr1, 32); + ptr1, len); __builtin_memcpy(((struct cmpfn_operands *)__afl_cmp_map->log[k])[hits].v1, - ptr2, 32); + ptr2, len); } @@ -1658,7 +1854,8 @@ static u8 *get_llvm_stdstring(u8 *string) { void __cmplog_rtn_gcc_stdstring_cstring(u8 *stdstring, u8 *cstring) { if (unlikely(!__afl_cmp_map)) return; - if (!area_is_mapped(stdstring, 32) || !area_is_mapped(cstring, 32)) return; + if (area_is_valid(stdstring, 32) <= 0 || area_is_valid(cstring, 32) <= 0) + return; __cmplog_rtn_hook(get_gcc_stdstring(stdstring), cstring); @@ -1667,7 +1864,7 @@ void __cmplog_rtn_gcc_stdstring_cstring(u8 *stdstring, u8 *cstring) { void __cmplog_rtn_gcc_stdstring_stdstring(u8 *stdstring1, u8 *stdstring2) { if (unlikely(!__afl_cmp_map)) return; - if (!area_is_mapped(stdstring1, 32) || !area_is_mapped(stdstring2, 32)) + if (area_is_valid(stdstring1, 32) <= 0 || area_is_valid(stdstring2, 32) <= 0) return; __cmplog_rtn_hook(get_gcc_stdstring(stdstring1), @@ -1678,7 +1875,8 @@ void __cmplog_rtn_gcc_stdstring_stdstring(u8 *stdstring1, u8 *stdstring2) { void __cmplog_rtn_llvm_stdstring_cstring(u8 *stdstring, u8 *cstring) { if (unlikely(!__afl_cmp_map)) return; - if (!area_is_mapped(stdstring, 32) || !area_is_mapped(cstring, 32)) return; + if (area_is_valid(stdstring, 32) <= 0 || area_is_valid(cstring, 32) <= 0) + return; __cmplog_rtn_hook(get_llvm_stdstring(stdstring), cstring); @@ -1687,7 +1885,7 @@ void __cmplog_rtn_llvm_stdstring_cstring(u8 *stdstring, u8 *cstring) { void __cmplog_rtn_llvm_stdstring_stdstring(u8 *stdstring1, u8 *stdstring2) { if (unlikely(!__afl_cmp_map)) return; - if (!area_is_mapped(stdstring1, 32) || !area_is_mapped(stdstring2, 32)) + if (area_is_valid(stdstring1, 32) <= 0 || area_is_valid(stdstring2, 32) <= 0) return; __cmplog_rtn_hook(get_llvm_stdstring(stdstring1), |